diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-09-23 16:06:29 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-09-24 11:41:55 +0000 |
commit | bac1035f131c0b95b75fb39ffd1a39652843de9f (patch) | |
tree | 44839fddbea648d54e4be47bcfbe4a5979bacd29 /chromium | |
parent | 271a6c3487a14599023a9106329505597638d793 (diff) | |
download | qtwebengine-chromium-bac1035f131c0b95b75fb39ffd1a39652843de9f.tar.gz |
BASELINE: Update Chromium to 77.0.3865.98
Change-Id: Ice85979eb8b64af9a3c649d719bec6ea14ac3bf7
Reviewed-by: Michael Brüning <michael.bruning@qt.io>
Diffstat (limited to 'chromium')
245 files changed, 4876 insertions, 2155 deletions
diff --git a/chromium/AUTHORS b/chromium/AUTHORS index d65fb3966b8..ba8282073f6 100644 --- a/chromium/AUTHORS +++ b/chromium/AUTHORS @@ -74,6 +74,7 @@ Andrei Parvu <andrei.prv@gmail.com> Andrei Parvu <parvu@adobe.com> Andrew Boyarshin <andrew.boyarshin@gmail.com> Andrew Brampton <me@bramp.net> +Andrew Brindamour <abrindamour@bluejeans.com> Andrew Hung <andrhung@amazon.com> Andrew Jorgensen <ajorgens@amazon.com> Andrew MacPherson <andrew.macpherson@soundtrap.com> @@ -421,6 +422,7 @@ Jianjun Zhu <jianjun.zhu@intel.com> Jianneng Zhong <muzuiget@gmail.com> Jiawei Shao <jiawei.shao@intel.com> Jie Chen <jie.a.chen@intel.com> +Jihan Chao <jihan@bluejeans.com> Jihoon Chung <j.c@navercorp.com> Jihoon Chung <jihoon@gmail.com> Jihun Brent Kim <devgrapher@gmail.com> diff --git a/chromium/DEPS b/chromium/DEPS index 24627c6c404..d6d3cb1f992 100644 --- a/chromium/DEPS +++ b/chromium/DEPS @@ -146,11 +146,11 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '2417cee95d9097a19d759a2267d4c3e51786e873', + 'skia_revision': 'a10014304cba4f24b7af17191f59490faa8aee77', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': '1e6ebba9def991e536159fa658bf5564c054733f', + 'v8_revision': '027689dbfcb2a9bbc8ceec4db2631c558e879633', # 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. @@ -1417,7 +1417,7 @@ deps = { Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@f40660faddf716870875beaac3f96911a9c4a554', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@18095bdf3eb60c3e121aac53a2c92eba6c37c3a9', 'condition': 'checkout_src_internal', }, diff --git a/chromium/build/toolchain/win/setup_toolchain.py b/chromium/build/toolchain/win/setup_toolchain.py index ef8aeda5645..9ee69df7d21 100644 --- a/chromium/build/toolchain/win/setup_toolchain.py +++ b/chromium/build/toolchain/win/setup_toolchain.py @@ -28,15 +28,21 @@ def _ExtractImportantEnvironment(output_of_set): """Extracts environment variables required for the toolchain to run from a textual dump output by the cmd.exe 'set' command.""" envvars_to_save = ( + 'cipd_cache_dir', # needed by vpython + 'homedrive', # needed by vpython + 'homepath', # needed by vpython 'goma_.*', # TODO(scottmg): This is ugly, but needed for goma. 'include', 'lib', 'libpath', + 'luci_context', # needed by vpython 'path', 'pathext', 'systemroot', 'temp', 'tmp', + 'userprofile', # needed by vpython + 'vpython_virtualenv_root' # needed by vpython ) env = {} # This occasionally happens and leads to misleading SYSTEMROOT error messages diff --git a/chromium/build/util/LASTCHANGE b/chromium/build/util/LASTCHANGE index a95aad93539..033ddb4ac97 100644 --- a/chromium/build/util/LASTCHANGE +++ b/chromium/build/util/LASTCHANGE @@ -1 +1 @@ -LASTCHANGE=f7668708a327d2ab897cd802bbbc8a8b67283ec8-refs/branch-heads/3865@{#680} +LASTCHANGE=89700f0d311d189a766a3532c1e6de2c94d429f9-refs/branch-heads/3865@{#842} diff --git a/chromium/build/util/LASTCHANGE.committime b/chromium/build/util/LASTCHANGE.committime index fc9ffb58c2e..27cab847d5e 100644 --- a/chromium/build/util/LASTCHANGE.committime +++ b/chromium/build/util/LASTCHANGE.committime @@ -1 +1 @@ -1567125764
\ No newline at end of file +1569197526
\ No newline at end of file diff --git a/chromium/chrome/VERSION b/chromium/chrome/VERSION index 5e9ec237228..2ba5d0d4c2a 100644 --- a/chromium/chrome/VERSION +++ b/chromium/chrome/VERSION @@ -1,4 +1,4 @@ MAJOR=77 MINOR=0 BUILD=3865 -PATCH=59 +PATCH=98 diff --git a/chromium/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chromium/chrome/android/features/tab_ui/tab_management_java_sources.gni index d287f692074..e524786980c 100644 --- a/chromium/chrome/android/features/tab_ui/tab_management_java_sources.gni +++ b/chromium/chrome/android/features/tab_ui/tab_management_java_sources.gni @@ -7,6 +7,7 @@ import( public_tab_management_java_sources = [ "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurface.java", + "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_groups/EmptyTabGroupModelFilterObserver.java", "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilter.java", "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SilenceLintErrors.java", "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUi.java", diff --git a/chromium/chrome/app/resources/chromium_strings_sl.xtb b/chromium/chrome/app/resources/chromium_strings_sl.xtb index 7639f108ba8..44ad5806bcf 100644 --- a/chromium/chrome/app/resources/chromium_strings_sl.xtb +++ b/chromium/chrome/app/resources/chromium_strings_sl.xtb @@ -36,7 +36,7 @@ Nekatere funkcije morda niso na voljo in spremembe nastavitev ne bodo shranjene. <translation id="2347108572062610441">Zaradi te razširitve se je spremenila stran, ki je prikazana, ko zaženete Chromium.</translation> <translation id="2396765026452590966">Zaradi razširitve »<ph name="EXTENSION_NAME" />« se je spremenila stran, ki je prikazana, ko zaženete Chromium.</translation> <translation id="2483889755041906834">V Chromiumu</translation> -<translation id="2485422356828889247">Odstranjevanje</translation> +<translation id="2485422356828889247">Odmeščanje</translation> <translation id="2527042973354814951">Znova zaženite Chromium, če želite omogočiti <ph name="PLUGIN_NAME" /></translation> <translation id="2535480412977113886">Chromium OS ni mogel sinhronizirati podatkov, ker so podatki za prijavo v račun zastareli.</translation> <translation id="2560420686485554789">Chromium potrebuje za prenos datotek dostop do shrambe</translation> diff --git a/chromium/chrome/app/resources/generated_resources_es.xtb b/chromium/chrome/app/resources/generated_resources_es.xtb index b3d14f2c6cb..a124286b6ed 100644 --- a/chromium/chrome/app/resources/generated_resources_es.xtb +++ b/chromium/chrome/app/resources/generated_resources_es.xtb @@ -1672,7 +1672,7 @@ Si no cambias la configuración predeterminada, <ph name="USER_DISPLAY_NAME" /> <translation id="3493486281776271508">Se necesita conexión a Internet</translation> <translation id="3493881266323043047">Validez</translation> <translation id="3494769164076977169">Preguntar cuando un sitio intente descargar archivos automáticamente después del primer archivo (recomendado)</translation> -<translation id="3495660573538963482">Configuración del Asistente de Google</translation> +<translation id="3495660573538963482">Ajustes del Asistente de Google</translation> <translation id="3496213124478423963">Alejar</translation> <translation id="3497560059572256875">Compartir doodle</translation> <translation id="3505030558724226696">Revocar acceso al dispositivo</translation> diff --git a/chromium/chrome/app/resources/generated_resources_kn.xtb b/chromium/chrome/app/resources/generated_resources_kn.xtb index 17c843e5d5d..964c9a5b207 100644 --- a/chromium/chrome/app/resources/generated_resources_kn.xtb +++ b/chromium/chrome/app/resources/generated_resources_kn.xtb @@ -3318,7 +3318,7 @@ <translation id="5959471481388474538">ನೆಟ್ವರ್ಕ್ ಲಭ್ಯವಿಲ್ಲ</translation> <translation id="595959584676692139">ಈ ವಿಸ್ತರಣೆಯನ್ನು ಬಳಸಲು ಪುಟವನ್ನು ಪುನಃ ಲೋಡ್ ಮಾಡಿ</translation> <translation id="5963453369025043595"><ph name="NUM_HANDLES" /> (<ph name="NUM_KILOBYTES_LIVE" /> ಪೀಕ್)</translation> -<translation id="5965661248935608907">ನೀವು ಹೋಮ್ ಬಟನ್ ಕ್ಲಿಕ್ ಮಾಡಿದಾಗ ಅಥವಾ ಓಮ್ನಿಬಾಕ್ಸ್ನಿಂದ ಹುಡುಕಿದಾಗ ತೋರಿಸಬೇಕಾದ ಪುಟವನ್ನು ಕೂಡಾ ಇದು ನಿಯಂತ್ರಿಸುತ್ತದೆ.</translation> +<translation id="5965661248935608907">ನೀವು ಹೋಮ್ ಬಟನ್ ಕ್ಲಿಕ್ ಮಾಡಿದಾಗ ಅಥವಾ ಆಮ್ನಿಬಾಕ್ಸ್ ನಿಂದ ಹುಡುಕಿದಾಗ ತೋರಿಸಬೇಕಾದ ಪುಟವನ್ನು ಕೂಡಾ ಇದು ನಿಯಂತ್ರಿಸುತ್ತದೆ.</translation> <translation id="5971037678316050792">ಬ್ಲೂಟೂತ್ ಅಡಾಪ್ಟರ್ ಸ್ಥಿತಿ ಮತ್ತು ಜೋಡಿಸುವಿಕೆಯನ್ನು ನಿಯಂತ್ರಿಸಿ</translation> <translation id="5972017421290582825">MIDI ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ನಿರ್ವಹಿಸಿ...</translation> <translation id="597235323114979258">ಇನ್ನಷ್ಟು ಗಮ್ಯಸ್ಥಾನಗಳನ್ನು ನೋಡಿ</translation> diff --git a/chromium/chrome/app/resources/generated_resources_sl.xtb b/chromium/chrome/app/resources/generated_resources_sl.xtb index e17e8e09cb1..6bc0b9e5264 100644 --- a/chromium/chrome/app/resources/generated_resources_sl.xtb +++ b/chromium/chrome/app/resources/generated_resources_sl.xtb @@ -1006,7 +1006,7 @@ <translation id="2480868415629598489">Spreminjanje podatkov, ki jih kopirate in prilepite</translation> <translation id="2482878487686419369">Obvestila</translation> <translation id="2484959914739448251">Če želite izbrisati podatke brskanja iz vseh sinhroniziranih naprav in Google Računa, <ph name="BEGIN_LINK" />vnesite geslo<ph name="END_LINK" />.</translation> -<translation id="2485422356828889247">Odstranjevanje</translation> +<translation id="2485422356828889247">Odmeščanje</translation> <translation id="2487067538648443797">Dodaj nov zaznamek</translation> <translation id="2489829450872380594">Naslednjič bo nov telefon odklenil to napravo <ph name="DEVICE_TYPE" />. Funkcijo Smart Lock lahko izklopite v nastavitvah.</translation> <translation id="2489918096470125693">Dodaj &mapo ...</translation> @@ -4901,7 +4901,7 @@ Datoteko s ključem shranite na varnem. Potrebovali jo boste za izdelavo novih r <translation id="8286036467436129157">Prijava</translation> <translation id="8286963743045814739">Z oknom brez beleženja zgodovine lahko zasebno brskate</translation> <translation id="8287902281644548111">Iskanje po klicu API-jul/URL-ju</translation> -<translation id="8288032458496410887">Odstranjevanje aplikacije <ph name="APP" /> …</translation> +<translation id="8288032458496410887">Odmeščanje aplikacije <ph name="APP" /> …</translation> <translation id="8291967909914612644">Država domačega ponudnika</translation> <translation id="8294431847097064396">Vir</translation> <translation id="8297006494302853456">Šibek</translation> diff --git a/chromium/chrome/app/resources/generated_resources_tr.xtb b/chromium/chrome/app/resources/generated_resources_tr.xtb index dbf9a619968..dad96e31314 100644 --- a/chromium/chrome/app/resources/generated_resources_tr.xtb +++ b/chromium/chrome/app/resources/generated_resources_tr.xtb @@ -2810,7 +2810,7 @@ Sunucunun mesajı: <ph name="SERVER_MSG" /></translation> <translation id="5222676887888702881">Çıkış</translation> <translation id="52232769093306234">Paketleme başarısız oldu.</translation> <translation id="5225324770654022472">Uygulamalar kısayolunu göster</translation> -<translation id="5227679487546032910">Varsayılan koyu camgöbeği avatar</translation> +<translation id="5227679487546032910">Varsayılan koyu turkuaz avatar</translation> <translation id="5228076606934445476">Cihazda bir sorun var. Bu hatadan kurtulmak için cihazı yeniden başlatıp tekrar denemeniz gerekir.</translation> <translation id="5229189185761556138">Giriş yöntemlerini yönet</translation> <translation id="5230516054153933099">Pencere</translation> diff --git a/chromium/chrome/app/resources/google_chrome_strings_sl.xtb b/chromium/chrome/app/resources/google_chrome_strings_sl.xtb index 30f0d40e17c..055ddf179f9 100644 --- a/chromium/chrome/app/resources/google_chrome_strings_sl.xtb +++ b/chromium/chrome/app/resources/google_chrome_strings_sl.xtb @@ -66,7 +66,7 @@ Nekatere funkcije morda niso na voljo in spremembe nastavitev ne bodo shranjene. <translation id="2348335408836342058">Chrome potrebuje dovoljenje za dostop do fotoaparata in mikrofona za to spletno mesto</translation> <translation id="2429317896000329049">Google Chrome ni mogel sinhronizirati podatkov, ker sinhronizacija ni na voljo za vašo domeno.</translation> <translation id="2467438592969358367">Google Chrome želi izvoziti gesla. Če želite omogočiti to, vnesite geslo za Windows.</translation> -<translation id="2485422356828889247">Odstranjevanje</translation> +<translation id="2485422356828889247">Odmeščanje</translation> <translation id="2534507159460261402">Google Pay (kopirano v Chrome)</translation> <translation id="2535429035253759792">Skrbnik prosi, da za uveljavitev te posodobitve znova zaženete Chrome</translation> <translation id="2580411288591421699">Ni mogoče namestiti različice Google Chroma, enake tisti, ki se trenutno izvaja. Zaprite Google Chrome in poskusite znova.</translation> diff --git a/chromium/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc b/chromium/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc index 9d49d65504c..633d8ff73d2 100644 --- a/chromium/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc +++ b/chromium/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc @@ -13,9 +13,12 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/chromeos/delegate_to_browser_gpu_service_accelerator_factory.h" #include "content/public/browser/render_frame_host.h" -#include "content/public/browser/video_capture_service.h" +#include "content/public/browser/system_connector.h" #include "mojo/public/cpp/bindings/strong_binding.h" +#include "services/service_manager/public/cpp/connector.h" +#include "services/video_capture/public/mojom/constants.mojom.h" #include "services/video_capture/public/mojom/device_factory.mojom.h" +#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" namespace extensions { @@ -65,18 +68,22 @@ void MediaPerceptionAPIDelegateChromeOS::LoadCrOSComponent( base::BindOnce(OnLoadComponent, std::move(load_callback))); } -void MediaPerceptionAPIDelegateChromeOS::BindVideoSourceProvider( - mojo::PendingReceiver<video_capture::mojom::VideoSourceProvider> receiver) { +void MediaPerceptionAPIDelegateChromeOS:: + BindDeviceFactoryProviderToVideoCaptureService( + video_capture::mojom::DeviceFactoryProviderPtr* provider) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + // In unit test environments, there may not be any connector. + service_manager::Connector* connector = content::GetSystemConnector(); + if (!connector) + return; + connector->BindInterface(video_capture::mojom::kServiceName, provider); + video_capture::mojom::AcceleratorFactoryPtr accelerator_factory; mojo::MakeStrongBinding( std::make_unique< content::DelegateToBrowserGpuServiceAcceleratorFactory>(), mojo::MakeRequest(&accelerator_factory)); - - auto& service = content::GetVideoCaptureService(); - service.InjectGpuDependencies(std::move(accelerator_factory)); - service.ConnectToVideoSourceProvider(std::move(receiver)); + (*provider)->InjectGpuDependencies(std::move(accelerator_factory)); } void MediaPerceptionAPIDelegateChromeOS::SetMediaPerceptionRequestHandler( diff --git a/chromium/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.h b/chromium/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.h index 45abc51201e..1d765110283 100644 --- a/chromium/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.h +++ b/chromium/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.h @@ -20,9 +20,8 @@ class MediaPerceptionAPIDelegateChromeOS void LoadCrOSComponent( const api::media_perception_private::ComponentType& type, LoadCrOSComponentCallback load_callback) override; - void BindVideoSourceProvider( - mojo::PendingReceiver<video_capture::mojom::VideoSourceProvider> receiver) - override; + void BindDeviceFactoryProviderToVideoCaptureService( + video_capture::mojom::DeviceFactoryProviderPtr* provider) override; void SetMediaPerceptionRequestHandler( MediaPerceptionRequestHandler handler) override; void ForwardMediaPerceptionRequest( diff --git a/chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json b/chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json index d58562d990f..079eca1a6bb 100644 --- a/chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json +++ b/chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json @@ -1,5 +1,5 @@ { - "x-version": 41, + "x-version": 42, "adobe-flash-player": { "mime_types": [ "application/futuresplash", @@ -10,9 +10,9 @@ ], "versions": [ { - "version": "32.0.0.207", + "version": "32.0.0.255", "status": "up_to_date", - "reference": "https://helpx.adobe.com/security/products/flash-player/apsb19-30.html" + "reference": "https://helpx.adobe.com/security/products/flash-player/apsb19-46.html" } ], "lang": "en-US", diff --git a/chromium/chrome/browser/resources/plugin_metadata/plugins_mac.json b/chromium/chrome/browser/resources/plugin_metadata/plugins_mac.json index eb67de53c11..0351e901fab 100644 --- a/chromium/chrome/browser/resources/plugin_metadata/plugins_mac.json +++ b/chromium/chrome/browser/resources/plugin_metadata/plugins_mac.json @@ -1,5 +1,5 @@ { - "x-version": 47, + "x-version": 48, "adobe-flash-player": { "mime_types": [ "application/futuresplash", @@ -7,9 +7,9 @@ ], "versions": [ { - "version": "32.0.0.207", + "version": "32.0.0.255", "status": "requires_authorization", - "reference": "https://helpx.adobe.com/security/products/flash-player/apsb19-30.html" + "reference": "https://helpx.adobe.com/security/products/flash-player/apsb19-46.html" } ], "lang": "en-US", diff --git a/chromium/chrome/browser/resources/plugin_metadata/plugins_win.json b/chromium/chrome/browser/resources/plugin_metadata/plugins_win.json index ecf05d88a44..5df5cacdb68 100644 --- a/chromium/chrome/browser/resources/plugin_metadata/plugins_win.json +++ b/chromium/chrome/browser/resources/plugin_metadata/plugins_win.json @@ -1,5 +1,5 @@ { - "x-version": 56, + "x-version": 57, "adobe-flash-player": { "mime_types": [ "application/futuresplash", @@ -7,9 +7,9 @@ ], "versions": [ { - "version": "32.0.0.207", + "version": "32.0.0.255", "status": "requires_authorization", - "reference": "https://helpx.adobe.com/security/products/flash-player/apsb19-30.html" + "reference": "https://helpx.adobe.com/security/products/flash-player/apsb19-46.html" } ], "lang": "en-US", diff --git a/chromium/chrome/common/webui_url_constants.cc b/chromium/chrome/common/webui_url_constants.cc index 4ea75d777ba..9aafec47750 100644 --- a/chromium/chrome/common/webui_url_constants.cc +++ b/chromium/chrome/common/webui_url_constants.cc @@ -168,6 +168,11 @@ const char kChromeUIVersionURL[] = "chrome://version/"; const char kChromeUIWelcomeHost[] = "welcome"; const char kChromeUIWelcomeURL[] = "chrome://welcome/"; +#if defined(OS_WIN) +// TODO(crbug.com/1003960): Remove when issue is resolved. +const char kChromeUIWelcomeWin10Host[] = "welcome-win10"; +#endif // defined(OS_WIN) + #if defined(OS_ANDROID) const char kChromeUIExploreSitesInternalsHost[] = "explore-sites-internals"; const char kChromeUIJavaCrashURL[] = "chrome://java-crash/"; diff --git a/chromium/chrome/common/webui_url_constants.h b/chromium/chrome/common/webui_url_constants.h index f6935a34532..7d60fd45b9b 100644 --- a/chromium/chrome/common/webui_url_constants.h +++ b/chromium/chrome/common/webui_url_constants.h @@ -170,6 +170,11 @@ extern const char kChromeUIVersionURL[]; extern const char kChromeUIWelcomeHost[]; extern const char kChromeUIWelcomeURL[]; +#if defined(OS_WIN) +// TODO(crbug.com/1003960): Remove when issue is resolved. +extern const char kChromeUIWelcomeWin10Host[]; +#endif // defined(OS_WIN) + #if defined(OS_ANDROID) extern const char kChromeUIExploreSitesInternalsHost[]; extern const char kChromeUIJavaCrashURL[]; diff --git a/chromium/chrome/credential_provider/gaiacp/gaia_resources.grd b/chromium/chrome/credential_provider/gaiacp/gaia_resources.grd index b44f6f614f2..54f712f3b92 100644 --- a/chromium/chrome/credential_provider/gaiacp/gaia_resources.grd +++ b/chromium/chrome/credential_provider/gaiacp/gaia_resources.grd @@ -106,6 +106,9 @@ <message name="IDS_NO_NETWORK" desc=""> Make sure you have a network connection and try again. </message> + <message name="IDS_FAILED_CREATE_LOGON_STUB" desc=""> + Unable to load Google sign in screen. Please contact your administrator. + </message> <message name="IDS_PASSWORD_UPDATE_NEEDED" desc=""> Your account password has changed. Please enter your current Windows password in order to sync your Windows account with your work account. </message> diff --git a/chromium/chrome/test/BUILD.gn b/chromium/chrome/test/BUILD.gn index 39a8fc30840..954efb53669 100644 --- a/chromium/chrome/test/BUILD.gn +++ b/chromium/chrome/test/BUILD.gn @@ -3067,6 +3067,7 @@ test("unit_tests") { "../browser/previews/previews_lite_page_predictor_unittest.cc", "../browser/previews/previews_lite_page_url_loader_interceptor_unittest.cc", "../browser/previews/previews_offline_helper_unittest.cc", + "../browser/previews/previews_service_render_view_unittest.cc", "../browser/previews/previews_service_unittest.cc", "../browser/previews/previews_top_host_provider_unittest.cc", "../browser/previews/previews_ui_tab_helper_unittest.cc", diff --git a/chromium/components/arc/ime/DEPS b/chromium/components/arc/ime/DEPS index 2b08010744b..ab2c42a02ab 100644 --- a/chromium/components/arc/ime/DEPS +++ b/chromium/components/arc/ime/DEPS @@ -7,5 +7,4 @@ include_rules = [ "+ui/gfx/geometry", # Revisit this dependency when crbug.com/890403 is resovled. "+ui/views", - "+ui/wm", ] diff --git a/chromium/components/arc/ime/arc_ime_service.cc b/chromium/components/arc/ime/arc_ime_service.cc index ebe0b002f9d..5b4a0c02da5 100644 --- a/chromium/components/arc/ime/arc_ime_service.cc +++ b/chromium/components/arc/ime/arc_ime_service.cc @@ -26,7 +26,6 @@ #include "ui/gfx/range/range.h" #include "ui/views/widget/widget.h" #include "ui/views/window/non_client_view.h" -#include "ui/wm/core/ime_util_chromeos.h" namespace arc { @@ -490,13 +489,6 @@ bool ArcImeService::GetTextFromRange(const gfx::Range& range, return true; } -void ArcImeService::EnsureCaretNotInRect(const gfx::Rect& rect_in_screen) { - if (focused_arc_window_ == nullptr) - return; - aura::Window* top_level_window = focused_arc_window_->GetToplevelWindow(); - wm::EnsureWindowNotInRect(top_level_window, rect_in_screen); -} - ui::TextInputMode ArcImeService::GetTextInputMode() const { return ui::TEXT_INPUT_MODE_DEFAULT; } diff --git a/chromium/components/arc/ime/arc_ime_service.h b/chromium/components/arc/ime/arc_ime_service.h index 2b3276ee623..c8f20c8c8fe 100644 --- a/chromium/components/arc/ime/arc_ime_service.h +++ b/chromium/components/arc/ime/arc_ime_service.h @@ -121,7 +121,6 @@ class ArcImeService : public KeyedService, bool GetEditableSelectionRange(gfx::Range* range) const override; bool GetTextFromRange(const gfx::Range& range, base::string16* text) const override; - void EnsureCaretNotInRect(const gfx::Rect& rect) override; // Overridden from ui::TextInputClient (with default implementation): // TODO(kinaba): Support each of these methods to the extent possible in @@ -141,6 +140,7 @@ class ArcImeService : public KeyedService, bool ChangeTextDirectionAndLayoutAlignment( base::i18n::TextDirection direction) override; void ExtendSelectionAndDelete(size_t before, size_t after) override; + void EnsureCaretNotInRect(const gfx::Rect& rect) override {} bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override; void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override { } diff --git a/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc b/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc index 3280327f767..84eecdc0f29 100644 --- a/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc @@ -908,14 +908,14 @@ TEST(CreditCardTest, b.set_guid(base::GenerateGUID()); b.set_origin(test::kEmptyOrigin); b.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("08")); - b.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2019")); + b.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2023")); EXPECT_TRUE(a.UpdateFromImportedCard(b, "en-US")); EXPECT_EQ("Chrome settings", a.origin()); EXPECT_EQ(ASCIIToUTF16("John Dillinger"), a.GetRawInfo(CREDIT_CARD_NAME_FULL)); EXPECT_EQ(ASCIIToUTF16("08"), a.GetRawInfo(CREDIT_CARD_EXP_MONTH)); - EXPECT_EQ(ASCIIToUTF16("2019"), a.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR)); + EXPECT_EQ(ASCIIToUTF16("2023"), a.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR)); } TEST(CreditCardTest, diff --git a/chromium/components/autofill_assistant/browser/actions/get_payment_information_action.cc b/chromium/components/autofill_assistant/browser/actions/get_payment_information_action.cc index fd31bccc08f..4f647963cc8 100644 --- a/chromium/components/autofill_assistant/browser/actions/get_payment_information_action.cc +++ b/chromium/components/autofill_assistant/browser/actions/get_payment_information_action.cc @@ -120,9 +120,9 @@ void GetPaymentInformationAction::OnGetPaymentInformation( autofill::AutofillProfile contact_profile; contact_profile.SetRawInfo( autofill::ServerFieldType::NAME_FULL, - base::ASCIIToUTF16(payment_information->payer_name)); + base::UTF8ToUTF16(payment_information->payer_name)); autofill::data_util::NameParts parts = autofill::data_util::SplitName( - base::ASCIIToUTF16(payment_information->payer_name)); + base::UTF8ToUTF16(payment_information->payer_name)); contact_profile.SetRawInfo(autofill::ServerFieldType::NAME_FIRST, parts.given); contact_profile.SetRawInfo(autofill::ServerFieldType::NAME_MIDDLE, @@ -131,10 +131,10 @@ void GetPaymentInformationAction::OnGetPaymentInformation( parts.family); contact_profile.SetRawInfo( autofill::ServerFieldType::EMAIL_ADDRESS, - base::ASCIIToUTF16(payment_information->payer_email)); + base::UTF8ToUTF16(payment_information->payer_email)); contact_profile.SetRawInfo( autofill::ServerFieldType::PHONE_HOME_WHOLE_NUMBER, - base::ASCIIToUTF16(payment_information->payer_phone)); + base::UTF8ToUTF16(payment_information->payer_phone)); if (!contact_details_proto.contact_details_name().empty()) { delegate_->GetClientMemory()->set_selected_address( contact_details_proto.contact_details_name(), diff --git a/chromium/components/dom_distiller/DEPS b/chromium/components/dom_distiller/DEPS index ea653ead7d3..9e2dcd6f5c0 100644 --- a/chromium/components/dom_distiller/DEPS +++ b/chromium/components/dom_distiller/DEPS @@ -8,6 +8,7 @@ include_rules = [ "+components/sync/protocol", "+components/sync_preferences", "+components/variations", + "+crypto", # For sha256 "+google", # For third_party/protobuf. "+third_party/dom_distiller_js", "+third_party/re2", diff --git a/chromium/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc b/chromium/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc index 53de6967aaf..27c0ac02e58 100644 --- a/chromium/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc +++ b/chromium/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc @@ -55,8 +55,7 @@ class DomDistillerViewerSource::RequestViewerHandle public content::WebContentsObserver { public: RequestViewerHandle(content::WebContents* web_contents, - const std::string& expected_scheme, - const std::string& expected_request_path, + const GURL& expected_url, DistilledPagePrefs* distilled_page_prefs, DistillerUIHandle* ui_handle); ~RequestViewerHandle() override; @@ -83,11 +82,8 @@ class DomDistillerViewerSource::RequestViewerHandle // cancelled. void Cancel(); - // The scheme hosting the current view request; - std::string expected_scheme_; - - // The query path for the current view request. - std::string expected_request_path_; + // The URL hosting the current view request; + const GURL expected_url_; // Whether the page is sufficiently initialized to handle updates from the // distiller. @@ -108,13 +104,11 @@ class DomDistillerViewerSource::RequestViewerHandle DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle( content::WebContents* web_contents, - const std::string& expected_scheme, - const std::string& expected_request_path, + const GURL& expected_url, DistilledPagePrefs* distilled_page_prefs, DistillerUIHandle* ui_handle) : DomDistillerRequestViewBase(distilled_page_prefs), - expected_scheme_(expected_scheme), - expected_request_path_(expected_request_path), + expected_url_(expected_url), waiting_for_page_ready_(true), distiller_ui_handle_(ui_handle) { content::WebContentsObserver::Observe(web_contents); @@ -146,9 +140,7 @@ void DomDistillerViewerSource::RequestViewerHandle::DidFinishNavigation( return; const GURL& navigation = navigation_handle->GetURL(); - bool expected_main_view_request = - navigation.SchemeIs(expected_scheme_) && - expected_request_path_ == navigation.query(); + bool expected_main_view_request = navigation == expected_url_; if (navigation_handle->IsSameDocument() || expected_main_view_request) { // In-page navigations, as well as the main view request can be ignored. if (expected_main_view_request) { @@ -260,21 +252,29 @@ void DomDistillerViewerSource::StartDataRequest( } } - // An empty |path| is invalid, but guard against it. If not empty, assume - // |path| starts with '?', which is stripped away. - const std::string path_after_query_separator = - path.size() > 0 ? path.substr(1) : ""; + // We need the host part to validate the parameter, but it's not available + // from |URLDataSource|. |web_contents| is the most convenient place to + // obtain the full URL. + // TODO(crbug.com/991888): pass GURL in URLDataSource::StartDataRequest(). + const std::string query = GURL("https://host/" + path).query(); + GURL request_url = web_contents->GetVisibleURL(); + // The query should match what's seen in |web_contents|. + // For javascript:window.open(), it's not the case, but it's not a supported + // use case. + if (request_url.query() != query || request_url.path() != "/") { + request_url = GURL(); + } RequestViewerHandle* request_viewer_handle = - new RequestViewerHandle(web_contents, scheme_, path_after_query_separator, + new RequestViewerHandle(web_contents, request_url, dom_distiller_service_->GetDistilledPagePrefs(), distiller_ui_handle_.get()); std::unique_ptr<ViewerHandle> viewer_handle = viewer::CreateViewRequest( - dom_distiller_service_, path, request_viewer_handle, + dom_distiller_service_, request_url, request_viewer_handle, web_contents->GetContainerBounds().size()); - GURL current_url(url_utils::GetValueForKeyInUrlPathQuery(path, kUrlKey)); + GURL current_url(url_utils::GetOriginalUrlFromDistillerUrl(request_url)); std::string unsafe_page_html = viewer::GetUnsafeArticleTemplateHtml( - url_utils::GetOriginalUrlFromDistillerUrl(current_url).spec(), + current_url.spec(), dom_distiller_service_->GetDistilledPagePrefs()->GetTheme(), dom_distiller_service_->GetDistilledPagePrefs()->GetFontFamily()); diff --git a/chromium/components/dom_distiller/core/url_utils.cc b/chromium/components/dom_distiller/core/url_utils.cc index cac08dd1b7c..cd7fc39bf94 100644 --- a/chromium/components/dom_distiller/core/url_utils.cc +++ b/chromium/components/dom_distiller/core/url_utils.cc @@ -8,11 +8,14 @@ #include "base/guid.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" #include "components/dom_distiller/core/url_constants.h" #include "components/grit/components_resources.h" +#include "crypto/sha2.h" #include "net/base/url_util.h" #include "ui/base/resource/resource_bundle.h" #include "url/gurl.h" +#include "url/url_util.h" namespace dom_distiller { @@ -21,6 +24,12 @@ namespace url_utils { namespace { const char kDummyInternalUrlPrefix[] = "chrome-distiller-internal://dummy/"; +const char kSeparator[] = "_"; + +std::string SHA256InHex(base::StringPiece str) { + std::string sha256 = crypto::SHA256HashString(str); + return base::ToLowerASCII(base::HexEncode(sha256.c_str(), sha256.size())); +} } // namespace @@ -31,28 +40,44 @@ const GURL GetDistillerViewUrlFromEntryId(const std::string& scheme, } const GURL GetDistillerViewUrlFromUrl(const std::string& scheme, - const GURL& view_url, + const GURL& url, int64_t start_time_ms) { - GURL url(scheme + "://" + base::GenerateGUID()); + GURL view_url(scheme + "://" + base::GenerateGUID() + kSeparator + + SHA256InHex(url.spec())); if (start_time_ms > 0) { - url = net::AppendOrReplaceQueryParameter( - url, kTimeKey, base::NumberToString(start_time_ms)); + view_url = net::AppendOrReplaceQueryParameter( + view_url, kTimeKey, base::NumberToString(start_time_ms)); } - return net::AppendOrReplaceQueryParameter(url, kUrlKey, view_url.spec()); + return net::AppendOrReplaceQueryParameter(view_url, kUrlKey, url.spec()); } const GURL GetOriginalUrlFromDistillerUrl(const GURL& url) { - if (!dom_distiller::url_utils::IsDistilledPage(url)) + if (!IsDistilledPage(url)) return url; std::string original_url_str; net::GetValueForKeyInQuery(url, kUrlKey, &original_url_str); - return GURL(original_url_str); + // Make sure kDomDistillerScheme is considered standard scheme for + // |GURL::host_piece()| to work correctly. + DCHECK(url::IsStandard(kDomDistillerScheme, + url::Component(0, strlen(kDomDistillerScheme)))); + std::vector<base::StringPiece> pieces = + base::SplitStringPiece(url.host_piece(), kSeparator, + base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + if (pieces.size() != 2) + return GURL(); + if (SHA256InHex(original_url_str) != pieces[1]) + return GURL(); + const GURL original_url(original_url_str); + if (!IsUrlDistillable(original_url)) + return GURL(); + + return original_url; } int64_t GetTimeFromDistillerUrl(const GURL& url) { - if (!dom_distiller::url_utils::IsDistilledPage(url)) + if (!IsDistilledPage(url)) return 0; std::string time_str; diff --git a/chromium/components/dom_distiller/core/url_utils.h b/chromium/components/dom_distiller/core/url_utils.h index d0beeb8a127..8f94a56d3a3 100644 --- a/chromium/components/dom_distiller/core/url_utils.h +++ b/chromium/components/dom_distiller/core/url_utils.h @@ -25,7 +25,9 @@ const GURL GetDistillerViewUrlFromUrl(const std::string& scheme, int64_t start_time_ms = 0); // Returns the original URL from the distilled URL. -// If the URL is not distilled, it is returned as is. +// If |distilled_url| is not distilled, it is returned as is. +// If |distilled_url| looks like distilled, but no original URL can be found, +// an empty, invalid URL is returned. const GURL GetOriginalUrlFromDistillerUrl(const GURL& distilled_url); // Returns the starting time from the distilled URL. diff --git a/chromium/components/dom_distiller/core/url_utils_unittest.cc b/chromium/components/dom_distiller/core/url_utils_unittest.cc index ed10e23b908..5dd2100491b 100644 --- a/chromium/components/dom_distiller/core/url_utils_unittest.cc +++ b/chromium/components/dom_distiller/core/url_utils_unittest.cc @@ -5,8 +5,10 @@ #include "components/dom_distiller/core/url_utils.h" #include "components/dom_distiller/core/url_constants.h" +#include "net/base/url_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" +#include "url/url_util.h" namespace dom_distiller { @@ -15,9 +17,12 @@ namespace url_utils { TEST(DomDistillerUrlUtilsTest, TestPathUtil) { const std::string single_key = "mypath?foo=bar"; EXPECT_EQ("bar", GetValueForKeyInUrlPathQuery(single_key, "foo")); + const std::string two_keys = "mypath?key1=foo&key2=bar"; EXPECT_EQ("foo", GetValueForKeyInUrlPathQuery(two_keys, "key1")); EXPECT_EQ("bar", GetValueForKeyInUrlPathQuery(two_keys, "key2")); + + // First occurrence wins. const std::string multiple_same_key = "mypath?key=foo&key=bar"; EXPECT_EQ("foo", GetValueForKeyInUrlPathQuery(multiple_same_key, "key")); } @@ -41,16 +46,43 @@ TEST(DomDistillerUrlUtilsTest, TestGetValueForKeyInUrlPathQuery) { EXPECT_EQ("foo", GetValueForKeyInUrlPathQuery(valid_url_two_keys, "key")); } -std::string ThroughDistiller(const std::string& url) { - return GetOriginalUrlFromDistillerUrl( - GetDistillerViewUrlFromUrl(kDomDistillerScheme, GURL(url), 123)) - .spec(); +void AssertEqualExceptHost(const GURL& a, const GURL& b) { + url::Replacements<char> no_host; + no_host.ClearHost(); + EXPECT_EQ(a.ReplaceComponents(no_host), b.ReplaceComponents(no_host)); +} + +TEST(DomDistillerUrlUtilsTest, TestGetDistillerViewUrlFromUrl) { + AssertEqualExceptHost( + GURL("chrome-distiller://any/" + "?time=123&url=http%3A%2F%2Fexample.com%2Fpath%3Fq%3Dabc%26p%3D1%" + "23anchor"), + GetDistillerViewUrlFromUrl( + kDomDistillerScheme, GURL("http://example.com/path?q=abc&p=1#anchor"), + 123)); } std::string GetOriginalUrlFromDistillerUrl(const std::string& url) { return GetOriginalUrlFromDistillerUrl(GURL(url)).spec(); } +TEST(DomDistillerUrlUtilsTest, TestGetOriginalUrlFromDistillerUrl) { + EXPECT_EQ( + "http://example.com/path?q=abc&p=1#anchor", + GetOriginalUrlFromDistillerUrl( + "chrome-distiller://" + "any_" + "d091ebf8f841eae9ca23822c3d0f369c16d3748478d0b74111be176eb96722e5/" + "?time=123&url=http%3A%2F%2Fexample.com%2Fpath%3Fq%3Dabc%26p%3D1%" + "23anchor")); +} + +std::string ThroughDistiller(const std::string& url) { + return GetOriginalUrlFromDistillerUrl( + GetDistillerViewUrlFromUrl(kDomDistillerScheme, GURL(url), 123)) + .spec(); +} + TEST(DomDistillerUrlUtilsTest, TestDistillerEndToEnd) { // Tests a normal url. const std::string url = "http://example.com/"; @@ -65,8 +97,24 @@ TEST(DomDistillerUrlUtilsTest, TestDistillerEndToEnd) { // Tests a url with file:// scheme. const std::string url_file = "file:///home/userid/path/index.html"; - EXPECT_EQ(url_file, ThroughDistiller(url_file)); + EXPECT_EQ("", ThroughDistiller(url_file)); EXPECT_EQ(url_file, GetOriginalUrlFromDistillerUrl(url_file)); + + // Tests a nested url. + const std::string nested_url = + GetDistillerViewUrlFromUrl(kDomDistillerScheme, GURL(url)).spec(); + EXPECT_EQ("", ThroughDistiller(nested_url)); + EXPECT_EQ(url, GetOriginalUrlFromDistillerUrl(nested_url)); +} + +TEST(DomDistillerUrlUtilsTest, TestRejectInvalidURLs) { + const std::string url = "http://example.com/"; + const std::string url2 = "http://example.org/"; + const GURL view_url = + GetDistillerViewUrlFromUrl(kDomDistillerScheme, GURL(url), 123); + GURL bad_view_url = + net::AppendOrReplaceQueryParameter(view_url, kUrlKey, url2); + EXPECT_EQ(GURL(), GetOriginalUrlFromDistillerUrl(bad_view_url)); } } // namespace url_utils diff --git a/chromium/components/dom_distiller/core/viewer.cc b/chromium/components/dom_distiller/core/viewer.cc index 6f5739d8a54..6fa470261ba 100644 --- a/chromium/components/dom_distiller/core/viewer.cc +++ b/chromium/components/dom_distiller/core/viewer.cc @@ -250,17 +250,17 @@ const std::string GetJavaScript() { std::unique_ptr<ViewerHandle> CreateViewRequest( DomDistillerServiceInterface* dom_distiller_service, - const std::string& path, + const GURL& url, ViewRequestDelegate* view_request_delegate, const gfx::Size& render_view_size) { - std::string entry_id = - url_utils::GetValueForKeyInUrlPathQuery(path, kEntryIdKey); + if (!url_utils::IsDistilledPage(url)) { + return nullptr; + } + std::string entry_id = url_utils::GetValueForKeyInUrl(url, kEntryIdKey); bool has_valid_entry_id = !entry_id.empty(); entry_id = base::ToUpperASCII(entry_id); - std::string requested_url_str = - url_utils::GetValueForKeyInUrlPathQuery(path, kUrlKey); - GURL requested_url(requested_url_str); + GURL requested_url(url_utils::GetOriginalUrlFromDistillerUrl(url)); bool has_valid_url = url_utils::IsUrlDistillable(requested_url); if (has_valid_entry_id && has_valid_url) { diff --git a/chromium/components/dom_distiller/core/viewer.h b/chromium/components/dom_distiller/core/viewer.h index 724e95aa0d8..96269ddc897 100644 --- a/chromium/components/dom_distiller/core/viewer.h +++ b/chromium/components/dom_distiller/core/viewer.h @@ -12,6 +12,7 @@ #include "base/strings/string16.h" #include "components/dom_distiller/core/distilled_page_prefs.h" #include "ui/gfx/geometry/size.h" +#include "url/gurl.h" namespace dom_distiller { @@ -75,7 +76,7 @@ const std::string GetJavaScript(); // viewing distilled content based on the |path|. std::unique_ptr<ViewerHandle> CreateViewRequest( DomDistillerServiceInterface* dom_distiller_service, - const std::string& path, + const GURL& url, ViewRequestDelegate* view_request_delegate, const gfx::Size& render_view_size); diff --git a/chromium/components/dom_distiller/core/viewer_unittest.cc b/chromium/components/dom_distiller/core/viewer_unittest.cc index 1e69a43440c..493daad1ff2 100644 --- a/chromium/components/dom_distiller/core/viewer_unittest.cc +++ b/chromium/components/dom_distiller/core/viewer_unittest.cc @@ -9,11 +9,24 @@ #include "components/dom_distiller/core/dom_distiller_test_util.h" #include "components/dom_distiller/core/task_tracker.h" #include "components/dom_distiller/core/url_constants.h" +#include "components/dom_distiller/core/url_utils.h" +#include "net/base/url_util.h" #include "testing/gtest/include/gtest/gtest.h" +#include "url/url_util.h" namespace dom_distiller { -const char kTestScheme[] = "myscheme"; +namespace { + +const GURL GetDistillerViewUrlFromUrl(const std::string& url) { + return url_utils::GetDistillerViewUrlFromUrl(kDomDistillerScheme, GURL(url)); +} + +const GURL GetDistillerViewUrlFromEntryId(const std::string& id) { + return url_utils::GetDistillerViewUrlFromEntryId(kDomDistillerScheme, id); +} + +} // namespace class FakeViewRequestDelegate : public ViewRequestDelegate { public: @@ -78,10 +91,10 @@ class DomDistillerViewerTest : public testing::Test { protected: std::unique_ptr<ViewerHandle> CreateViewRequest( - const std::string& path, + const GURL& url, ViewRequestDelegate* view_request_delegate) { - return viewer::CreateViewRequest(service_.get(), path, - view_request_delegate, gfx::Size()); + return viewer::CreateViewRequest(service_.get(), url, view_request_delegate, + gfx::Size()); } std::unique_ptr<TestDomDistillerService> service_; @@ -94,9 +107,8 @@ TEST_F(DomDistillerViewerTest, TestCreatingViewUrlRequest) { EXPECT_CALL(*service_, ViewUrlImpl()) .WillOnce(testing::Return(viewer_handle)); EXPECT_CALL(*service_, ViewEntryImpl()).Times(0); - CreateViewRequest( - std::string("?") + kUrlKey + "=http%3A%2F%2Fwww.example.com%2F", - view_request_delegate.get()); + CreateViewRequest(GetDistillerViewUrlFromUrl("http://www.example.com/"), + view_request_delegate.get()); } TEST_F(DomDistillerViewerTest, TestCreatingViewEntryRequest) { @@ -106,7 +118,7 @@ TEST_F(DomDistillerViewerTest, TestCreatingViewEntryRequest) { EXPECT_CALL(*service_, ViewEntryImpl()) .WillOnce(testing::Return(viewer_handle)); EXPECT_CALL(*service_, ViewUrlImpl()).Times(0); - CreateViewRequest(std::string("?") + kEntryIdKey + "=abc-def", + CreateViewRequest(GetDistillerViewUrlFromEntryId("abc-def"), view_request_delegate.get()); } @@ -116,19 +128,24 @@ TEST_F(DomDistillerViewerTest, TestCreatingInvalidViewRequest) { EXPECT_CALL(*service_, ViewEntryImpl()).Times(0); EXPECT_CALL(*service_, ViewUrlImpl()).Times(0); // Specify none of the required query parameters. - CreateViewRequest("?foo=bar", view_request_delegate.get()); + CreateViewRequest(GURL(std::string(kDomDistillerScheme) + "://host?foo=bar"), + view_request_delegate.get()); // Specify both of the required query parameters. - CreateViewRequest("?" + std::string(kUrlKey) + - "=http%3A%2F%2Fwww.example.com%2F&" + - std::string(kEntryIdKey) + "=abc-def", + CreateViewRequest(net::AppendOrReplaceQueryParameter( + GetDistillerViewUrlFromUrl("http://www.example.com/"), + kEntryIdKey, "abc-def"), view_request_delegate.get()); // Specify an internal Chrome page. - CreateViewRequest("?" + std::string(kUrlKey) + "=chrome%3A%2F%2Fsettings%2F", + CreateViewRequest(GetDistillerViewUrlFromUrl("chrome://settings/"), view_request_delegate.get()); // Specify a recursive URL. - CreateViewRequest("?" + std::string(kUrlKey) + "=" + - std::string(kTestScheme) + "%3A%2F%2Fabc-def%2F", + CreateViewRequest(GetDistillerViewUrlFromUrl( + GetDistillerViewUrlFromEntryId("abc-def").spec()), view_request_delegate.get()); + // Specify a non-distilled URL. + CreateViewRequest(GURL("https://example.com"), view_request_delegate.get()); + // Specify an empty URL. + CreateViewRequest(GURL(), view_request_delegate.get()); } DistilledPagePrefs* TestDomDistillerService::GetDistilledPagePrefs() { diff --git a/chromium/components/download/internal/common/download_create_info.cc b/chromium/components/download/internal/common/download_create_info.cc index b875d995273..20881b0be7b 100644 --- a/chromium/components/download/internal/common/download_create_info.cc +++ b/chromium/components/download/internal/common/download_create_info.cc @@ -31,7 +31,8 @@ DownloadCreateInfo::DownloadCreateInfo( accept_range(RangeRequestSupportType::kNoSupport), connection_info(net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN), method("GET"), - ukm_source_id(ukm::kInvalidSourceId) {} + ukm_source_id(ukm::kInvalidSourceId), + is_content_initiated(false) {} DownloadCreateInfo::DownloadCreateInfo() : DownloadCreateInfo(base::Time(), base::WrapUnique(new DownloadSaveInfo)) { diff --git a/chromium/components/download/internal/common/resource_downloader.cc b/chromium/components/download/internal/common/resource_downloader.cc index aaeef1a691f..15de44ecd42 100644 --- a/chromium/components/download/internal/common/resource_downloader.cc +++ b/chromium/components/download/internal/common/resource_downloader.cc @@ -139,7 +139,8 @@ ResourceDownloader::ResourceDownloader( tab_referrer_url_(tab_referrer_url), delegate_task_runner_(task_runner), url_loader_factory_getter_(std::move(url_loader_factory_getter)), - url_security_policy_(url_security_policy) { + url_security_policy_(url_security_policy), + is_content_initiated_(false) { RequestWakeLock(connector.get()); } @@ -152,6 +153,7 @@ void ResourceDownloader::Start( callback_ = download_url_parameters->callback(); upload_callback_ = download_url_parameters->upload_callback(); guid_ = download_url_parameters->guid(); + is_content_initiated_ = download_url_parameters->content_initiated(); // Set up the URLLoaderClient. url_loader_client_ = std::make_unique<DownloadResponseHandler>( @@ -235,6 +237,7 @@ void ResourceDownloader::OnResponseStarted( download_create_info->render_process_id = render_process_id_; download_create_info->render_frame_id = render_frame_id_; download_create_info->has_user_gesture = resource_request_->has_user_gesture; + download_create_info->is_content_initiated = is_content_initiated_; delegate_task_runner_->PostTask( FROM_HERE, diff --git a/chromium/components/download/internal/common/resource_downloader.h b/chromium/components/download/internal/common/resource_downloader.h index ed355ee35be..230269b856d 100644 --- a/chromium/components/download/internal/common/resource_downloader.h +++ b/chromium/components/download/internal/common/resource_downloader.h @@ -169,6 +169,9 @@ class COMPONENTS_DOWNLOAD_EXPORT ResourceDownloader // Used to check if the URL is safe to request. URLSecurityPolicy url_security_policy_; + // Whether download is initated by the content on the page. + bool is_content_initiated_; + // Used to keep the system from sleeping while a download is ongoing. If the // system enters power saving mode while a download is alive, it can cause // download to be interrupted. diff --git a/chromium/components/download/public/common/download_create_info.h b/chromium/components/download/public/common/download_create_info.h index 0e97e309a85..84553dc1b97 100644 --- a/chromium/components/download/public/common/download_create_info.h +++ b/chromium/components/download/public/common/download_create_info.h @@ -178,6 +178,9 @@ struct COMPONENTS_DOWNLOAD_EXPORT DownloadCreateInfo { // Source of the download, used in metrics. DownloadSource download_source = DownloadSource::UNKNOWN; + // Whether download is initated by the content on the page. + bool is_content_initiated; + private: DISALLOW_COPY_AND_ASSIGN(DownloadCreateInfo); }; diff --git a/chromium/components/exo/client_controlled_shell_surface.cc b/chromium/components/exo/client_controlled_shell_surface.cc index 1ef2e94d6dd..77a07221861 100644 --- a/chromium/components/exo/client_controlled_shell_surface.cc +++ b/chromium/components/exo/client_controlled_shell_surface.cc @@ -811,6 +811,8 @@ void ClientControlledShellSurface::SetWidgetBounds(const gfx::Rect& bounds) { display::Display display; if (screen->GetDisplayWithDisplayId(display_id_, &display)) { bool is_display_stale = display_id_ != current_display.id(); + LOG(ERROR) << "DisplayId:" << display_id_ + << ", current:" << current_display.id(); // Preserve widget bounds until client acknowledges display move. if (preserve_widget_bounds_ && is_display_stale) @@ -844,6 +846,7 @@ void ClientControlledShellSurface::SetWidgetBounds(const gfx::Rect& bounds) { bool set_bounds_locally = GetWindowState()->is_dragged() && !is_display_move_pending; + LOG(ERROR) << "Updating Locally"; if (set_bounds_locally || client_controlled_state_->set_bounds_locally()) { // Convert from screen to display coordinates. @@ -858,6 +861,7 @@ void ClientControlledShellSurface::SetWidgetBounds(const gfx::Rect& bounds) { UpdateSurfaceBounds(); return; } + LOG(ERROR) << "Updating Remotely"; { ScopedSetBoundsLocally scoped_set_bounds(this); @@ -865,6 +869,8 @@ void ClientControlledShellSurface::SetWidgetBounds(const gfx::Rect& bounds) { } if (bounds != adjusted_bounds || is_display_move_pending) { + LOG(ERROR) << "Sending Bounds:" << bounds.ToString() + << ", adjusted=" << adjusted_bounds.ToString(); // Notify client that bounds were adjusted or window moved across displays. auto state_type = GetWindowState()->GetStateType(); OnBoundsChangeEvent(state_type, state_type, target_display.id(), @@ -937,13 +943,13 @@ bool ClientControlledShellSurface::OnPreWidgetCommit() { } ash::WindowState* window_state = GetWindowState(); - if (window_state->GetStateType() == pending_window_state_) { + state_changed_ = window_state->GetStateType() != pending_window_state_; + if (!state_changed_) { // Animate PIP window movement unless it is being dragged. if (window_state->IsPip() && !window_state->is_dragged()) { client_controlled_state_->set_next_bounds_change_animation_type( ash::ClientControlledState::kAnimationAnimated); } - return true; } @@ -1043,9 +1049,11 @@ void ClientControlledShellSurface::UpdateFrame() { bool enable_wide_frame = GetFrameView()->GetVisible() && window_state->IsMaximizedOrFullscreenOrPinned() && work_area.width() != geometry().width(); - + bool update_frame = state_changed_; + state_changed_ = false; if (enable_wide_frame) { if (!wide_frame_) { + update_frame = true; wide_frame_ = std::make_unique<ash::WideFrameView>(widget_); ash::ImmersiveFullscreenController::EnableForWidget(widget_, false); wide_frame_->Init(immersive_fullscreen_controller_.get()); @@ -1063,6 +1071,7 @@ void ClientControlledShellSurface::UpdateFrame() { } } else { if (wide_frame_) { + update_frame = true; ash::ImmersiveFullscreenController::EnableForWidget(widget_, false); wide_frame_.reset(); GetFrameView()->InitImmersiveFullscreenControllerForView( @@ -1077,7 +1086,8 @@ void ClientControlledShellSurface::UpdateFrame() { // The autohide should be applied when the window state is in // maximzied, fullscreen or pinned. Update the auto hide state // inside commit. - UpdateAutoHideFrame(); + if (update_frame) + UpdateAutoHideFrame(); } void ClientControlledShellSurface::UpdateCaptionButtonModel() { diff --git a/chromium/components/exo/client_controlled_shell_surface.h b/chromium/components/exo/client_controlled_shell_surface.h index 5600be9419a..3fccb24a28c 100644 --- a/chromium/components/exo/client_controlled_shell_surface.h +++ b/chromium/components/exo/client_controlled_shell_surface.h @@ -335,6 +335,9 @@ class ClientControlledShellSurface : public ShellSurfaceBase, bool ignore_bounds_change_request_ = false; + // True if the window state has changed during the commit. + bool state_changed_ = false; + DISALLOW_COPY_AND_ASSIGN(ClientControlledShellSurface); }; diff --git a/chromium/components/exo/client_controlled_shell_surface_unittest.cc b/chromium/components/exo/client_controlled_shell_surface_unittest.cc index 9d18b869617..4749a904dce 100644 --- a/chromium/components/exo/client_controlled_shell_surface_unittest.cc +++ b/chromium/components/exo/client_controlled_shell_surface_unittest.cc @@ -477,6 +477,8 @@ TEST_F(ClientControlledShellSurfaceTest, ShadowStartMaximized) { } TEST_F(ClientControlledShellSurfaceTest, Frame) { + UpdateDisplay("800x600"); + gfx::Size buffer_size(256, 256); std::unique_ptr<Buffer> buffer( new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); @@ -484,7 +486,7 @@ TEST_F(ClientControlledShellSurfaceTest, Frame) { std::unique_ptr<Surface> surface(new Surface); gfx::Rect client_bounds(20, 50, 300, 200); - gfx::Rect fullscreen_bounds(0, 0, 800, 500); + gfx::Rect fullscreen_bounds(0, 0, 800, 600); // The window bounds is the client bounds + frame size. gfx::Rect normal_window_bounds(20, 18, 300, 232); @@ -508,13 +510,13 @@ TEST_F(ClientControlledShellSurfaceTest, Frame) { // Maximized shell_surface->SetMaximized(); - shell_surface->SetGeometry(gfx::Rect(0, 0, 800, 468)); + shell_surface->SetGeometry(gfx::Rect(0, 0, 800, 568)); surface->Commit(); EXPECT_TRUE(frame_view->GetVisible()); EXPECT_EQ(fullscreen_bounds, widget->GetWindowBoundsInScreen()); EXPECT_EQ( - gfx::Size(800, 468), + gfx::Size(800, 568), frame_view->GetClientBoundsForWindowBounds(fullscreen_bounds).size()); // AutoHide @@ -534,6 +536,15 @@ TEST_F(ClientControlledShellSurfaceTest, Frame) { EXPECT_EQ(fullscreen_bounds, frame_view->GetClientBoundsForWindowBounds(fullscreen_bounds)); + // Updating frame, then window state should still update the frame state. + surface->SetFrame(SurfaceFrameType::NORMAL); + surface->Commit(); + EXPECT_FALSE(frame_view->GetHeaderView()->GetVisible()); + + shell_surface->SetMaximized(); + surface->Commit(); + EXPECT_TRUE(frame_view->GetHeaderView()->GetVisible()); + // Restore to normal state. shell_surface->SetRestored(); shell_surface->SetGeometry(client_bounds); diff --git a/chromium/components/previews/core/previews_experiments.cc b/chromium/components/previews/core/previews_experiments.cc index 57c76ad649d..82e1272397e 100644 --- a/chromium/components/previews/core/previews_experiments.cc +++ b/chromium/components/previews/core/previews_experiments.cc @@ -382,6 +382,15 @@ bool ShouldExcludeMediaSuffix(const GURL& url) { return false; } +bool DetectDeferRedirectLoopsUsingCache() { + if (!IsDeferAllScriptPreviewsEnabled()) + return false; + + return GetFieldTrialParamByFeatureAsBool(features::kDeferAllScriptPreviews, + "detect_redirect_loop_using_cache", + true); +} + } // namespace params std::string GetStringNameForType(PreviewsType type) { diff --git a/chromium/components/previews/core/previews_experiments.h b/chromium/components/previews/core/previews_experiments.h index 44b6659d02c..464d011feed 100644 --- a/chromium/components/previews/core/previews_experiments.h +++ b/chromium/components/previews/core/previews_experiments.h @@ -206,6 +206,10 @@ bool ShouldOverrideNavigationCoinFlipToAllowed(); // Returns true if the given url matches an excluded media suffix. bool ShouldExcludeMediaSuffix(const GURL& url); +// Returns true if the logic to detect redirect loops with defer all script +// preview using a cache is enabled. +bool DetectDeferRedirectLoopsUsingCache(); + } // namespace params } // namespace previews diff --git a/chromium/components/printing/renderer/print_render_frame_helper.cc b/chromium/components/printing/renderer/print_render_frame_helper.cc index ef580254bd8..84c11dcdb4f 100644 --- a/chromium/components/printing/renderer/print_render_frame_helper.cc +++ b/chromium/components/printing/renderer/print_render_frame_helper.cc @@ -710,6 +710,8 @@ void PrintRenderFrameHelper::PrintHeaderAndFooter( options->SetDouble("height", page_size.height); options->SetDouble("topMargin", page_layout.margin_top); options->SetDouble("bottomMargin", page_layout.margin_bottom); + options->SetDouble("leftMargin", page_layout.margin_left); + options->SetDouble("rightMargin", page_layout.margin_right); options->SetInteger("pageNumber", page_number); options->SetInteger("totalPages", total_pages); options->SetString("url", params.url); diff --git a/chromium/components/printing/resources/print_header_footer_template_page.html b/chromium/components/printing/resources/print_header_footer_template_page.html index 5fee7a64f36..3c79b51abf6 100644 --- a/chromium/components/printing/resources/print_header_footer_template_page.html +++ b/chromium/components/printing/resources/print_header_footer_template_page.html @@ -61,6 +61,24 @@ </style> <script> +function getComputedStyleAsFloat(style, value) { + return parseFloat(style.getPropertyValue(value).slice(0, -2)); +} + +function elementIntersects(element, topPos, bottomPos, leftPos, rightPos) { + const rect = element.getBoundingClientRect(); + const style = window.getComputedStyle(element); + + // Only consider the size of |element|, so remove the padding from |rect|. + // The padding is used for positioning. + rect.top += getComputedStyleAsFloat(style, 'padding-top'); + rect.bottom -= getComputedStyleAsFloat(style, 'padding-bottom'); + rect.left += getComputedStyleAsFloat(style, 'padding-left'); + rect.right -= getComputedStyleAsFloat(style, 'padding-right'); + return leftPos < rect.right && rightPos > rect.left && topPos < rect.bottom && + bottomPos > rect.top; +} + function setupHeaderFooterTemplate(options) { const body = document.querySelector('body'); const header = document.querySelector('#header'); @@ -71,6 +89,11 @@ function setupHeaderFooterTemplate(options) { header.style.height = `${options.topMargin}px`; footer.style.height = `${options.bottomMargin}px`; + const topMargin = options.topMargin; + const bottomMargin = options.height - options.bottomMargin; + const leftMargin = options.leftMargin; + const rightMargin = options.width - options.rightMargin; + header.innerHTML = options['headerTemplate'] || ` <div class='date text left'></div> <div class='title text center'></div>`; @@ -86,6 +109,11 @@ function setupHeaderFooterTemplate(options) { element.textContent = options[cssClass]; if (options.isRtl) element.dir = 'rtl'; + + if (elementIntersects(element, topMargin, bottomMargin, leftMargin, + rightMargin)) { + element.style.visibility = 'hidden'; + } } } } diff --git a/chromium/components/safe_browsing/password_protection/password_protection_request.cc b/chromium/components/safe_browsing/password_protection/password_protection_request.cc index b93ac74d222..6919f8af1f2 100644 --- a/chromium/components/safe_browsing/password_protection/password_protection_request.cc +++ b/chromium/components/safe_browsing/password_protection/password_protection_request.cc @@ -77,7 +77,8 @@ PasswordProtectionRequest::PasswordProtectionRequest( bool password_field_exists, PasswordProtectionService* pps, int request_timeout_in_ms) - : web_contents_(web_contents), + : content::WebContentsObserver(web_contents), + web_contents_(web_contents), main_frame_url_(main_frame_url), password_form_action_(password_form_action), password_form_frame_url_(password_form_frame_url), @@ -509,4 +510,8 @@ void PasswordProtectionRequest::HandleDeferredNavigations() { throttles_.clear(); } +void PasswordProtectionRequest::WebContentsDestroyed() { + Cancel(/*timed_out=*/false); +} + } // namespace safe_browsing diff --git a/chromium/components/safe_browsing/password_protection/password_protection_request.h b/chromium/components/safe_browsing/password_protection/password_protection_request.h index 2dc4b4a0cba..e823d874532 100644 --- a/chromium/components/safe_browsing/password_protection/password_protection_request.h +++ b/chromium/components/safe_browsing/password_protection/password_protection_request.h @@ -17,6 +17,7 @@ #include "components/safe_browsing/password_protection/password_protection_service.h" #include "components/safe_browsing/proto/csd.pb.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/web_contents_observer.h" #include "third_party/skia/include/core/SkBitmap.h" class GURL; @@ -50,10 +51,10 @@ using password_manager::metrics_util::PasswordType; // (8) | UI | On receiving response, handle response and finish. // | | On request timeout, cancel request. // | | On deletion of |password_protection_service_|, cancel request. -class PasswordProtectionRequest - : public base::RefCountedThreadSafe< - PasswordProtectionRequest, - content::BrowserThread::DeleteOnUIThread> { +class PasswordProtectionRequest : public base::RefCountedThreadSafe< + PasswordProtectionRequest, + content::BrowserThread::DeleteOnUIThread>, + public content::WebContentsObserver { public: PasswordProtectionRequest(content::WebContents* web_contents, const GURL& main_frame_url, @@ -116,6 +117,9 @@ class PasswordProtectionRequest // Cancels navigation if there is modal warning showing, resumes it otherwise. void HandleDeferredNavigations(); + // WebContentsObserver implementation + void WebContentsDestroyed() override; + protected: friend class base::RefCountedThreadSafe<PasswordProtectionRequest>; @@ -125,7 +129,7 @@ class PasswordProtectionRequest friend class base::DeleteHelper<PasswordProtectionRequest>; friend class PasswordProtectionServiceTest; friend class ChromePasswordProtectionServiceTest; - virtual ~PasswordProtectionRequest(); + ~PasswordProtectionRequest() override; // Start checking the whitelist. void CheckWhitelist(); diff --git a/chromium/components/safe_browsing/password_protection/password_protection_service_unittest.cc b/chromium/components/safe_browsing/password_protection/password_protection_service_unittest.cc index 2ee6343bc35..2c6e54609c1 100644 --- a/chromium/components/safe_browsing/password_protection/password_protection_service_unittest.cc +++ b/chromium/components/safe_browsing/password_protection/password_protection_service_unittest.cc @@ -1207,6 +1207,14 @@ TEST_P(PasswordProtectionServiceTest, TestRequestCancelNotOnTimeout) { EXPECT_EQ(0U, GetNumberOfNavigationThrottles()); } +TEST_P(PasswordProtectionServiceTest, TestWebContentsDestroyed) { + content::WebContents* web_contents = GetWebContents(); + InitializeAndStartPasswordOnFocusRequest( + false /* match whitelist */, 10000 /* timeout in ms */, web_contents); + delete web_contents; + base::RunLoop().RunUntilIdle(); +} + INSTANTIATE_TEST_SUITE_P(Regular, PasswordProtectionServiceTest, ::testing::Values(false)); diff --git a/chromium/components/strings/components_strings_id.xtb b/chromium/components/strings/components_strings_id.xtb index 169d254bffa..f77532e83c5 100644 --- a/chromium/components/strings/components_strings_id.xtb +++ b/chromium/components/strings/components_strings_id.xtb @@ -190,7 +190,7 @@ <translation id="187918866476621466">Buka halaman awal</translation> <translation id="1883255238294161206">Ciutkan daftar</translation> <translation id="1898423065542865115">Pemfilteran</translation> -<translation id="1914326953223720820">Layanan Unzip</translation> +<translation id="1914326953223720820">Layanan Ekstraksi File</translation> <translation id="1916770123977586577">Untuk menerapkan setelan yang telah diupdate pada situs ini, muat ulang halaman ini</translation> <translation id="1919345977826869612">Iklan</translation> <translation id="1919367280705858090">Mendapatkan bantuan terkait pesan error tertentu</translation> diff --git a/chromium/components/strings/components_strings_no.xtb b/chromium/components/strings/components_strings_no.xtb index 818edbd17a6..d16e3a8d4b7 100644 --- a/chromium/components/strings/components_strings_no.xtb +++ b/chromium/components/strings/components_strings_no.xtb @@ -388,7 +388,7 @@ <translation id="2969319727213777354">Klokken må være riktig stilt før du kan opprette sikre tilkoblinger. Grunnen til dette er at sertifikatene nettsteder identifiserer seg med, bare er gyldige i visse tidsperioder. Ettersom klokken på enheten din er feil, kan ikke Google Chrome bekrefte disse sertifikatene.</translation> <translation id="2972581237482394796">Gjø&r om</translation> <translation id="2977665033722899841"><ph name="ROW_NAME" />, for øyeblikket valgt. <ph name="ROW_CONTENT" /></translation> -<translation id="2982481275546140226">Fjern data</translation> +<translation id="2982481275546140226">Slett data</translation> <translation id="2985306909656435243">Hvis du slår på dette alternativet, lagrer Chromium en kopi av kortet ditt på denne enheten, slik at det går raskere å fylle ut skjemaer.</translation> <translation id="2985398929374701810">Angi en gyldig adresse</translation> <translation id="2986368408720340940">Denne hentemetoden er ikke tilgjengelig. Prøv en annen metode.</translation> diff --git a/chromium/components/strings/components_strings_tr.xtb b/chromium/components/strings/components_strings_tr.xtb index bbdefb4ef4d..225a4344334 100644 --- a/chromium/components/strings/components_strings_tr.xtb +++ b/chromium/components/strings/components_strings_tr.xtb @@ -1110,7 +1110,7 @@ <translation id="7050187094878475250"><ph name="DOMAIN" /> alan adına erişmeyi denediniz, ancak sunucu, geçerlilik dönemi güvenilir olmayacak kadar uzun olan bir sertifika sundu.</translation> <translation id="705310974202322020">{NUM_CARDS,plural, =1{Bu kart şu anda kaydedilemiyor}other{Bu kartlar şu anda kaydedilemiyor}}</translation> <translation id="7053983685419859001">Engelle</translation> -<translation id="7062635574500127092">Turkuvaz</translation> +<translation id="7062635574500127092">Turkuaz</translation> <translation id="7064851114919012435">İletişim bilgileri</translation> <translation id="7075452647191940183">İstek çok büyük</translation> <translation id="7079718277001814089">Bu site kötü amaçlı yazılım içeriyor</translation> diff --git a/chromium/components/variations/service/variations_field_trial_creator.cc b/chromium/components/variations/service/variations_field_trial_creator.cc index 5abe50de8a6..c35a39c14b7 100644 --- a/chromium/components/variations/service/variations_field_trial_creator.cc +++ b/chromium/components/variations/service/variations_field_trial_creator.cc @@ -388,9 +388,11 @@ std::string VariationsFieldTrialCreator::GetShortHardwareClass() { board.resize(index); return base::ToUpperASCII(board); +#elif defined(OS_ANDROID) + return base::SysInfo::HardwareModelName(); #else return std::string(); -#endif // OS_CHROMEOS +#endif } bool VariationsFieldTrialCreator::LoadSeed(VariationsSeed* seed, diff --git a/chromium/components/variations/service/variations_field_trial_creator.h b/chromium/components/variations/service/variations_field_trial_creator.h index 5159583fc77..40456a0c3c1 100644 --- a/chromium/components/variations/service/variations_field_trial_creator.h +++ b/chromium/components/variations/service/variations_field_trial_creator.h @@ -123,8 +123,8 @@ class VariationsFieldTrialCreator { const std::string& application_locale() const { return application_locale_; } // Returns the short hardware class value used to evaluate variations hardware - // class filters. Only implemented on CrOS - returns empty string on other - // platforms. + // class filters. Only implemented on CrOS and Android - returns empty string + // on other platforms. static std::string GetShortHardwareClass(); private: diff --git a/chromium/components/variations/service/variations_field_trial_creator_unittest.cc b/chromium/components/variations/service/variations_field_trial_creator_unittest.cc index 8da72309ea6..4925a3b0fd4 100644 --- a/chromium/components/variations/service/variations_field_trial_creator_unittest.cc +++ b/chromium/components/variations/service/variations_field_trial_creator_unittest.cc @@ -519,6 +519,24 @@ TEST_F(FieldTrialCreatorTest, SetupFieldTrials_LoadsCountryOnFirstRun) { EXPECT_EQ(kTestSeedExperimentName, base::FieldTrialList::FindFullName(kTestSeedStudyName)); } + +// Tests that the hardware class is set on Android. +TEST_F(FieldTrialCreatorTest, ClientFilterableState_HardwareClass) { + testing::NiceMock<MockSafeSeedManager> safe_seed_manager(&prefs_); + ON_CALL(safe_seed_manager, ShouldRunInSafeMode()) + .WillByDefault(Return(false)); + + TestVariationsServiceClient variations_service_client; + TestVariationsFieldTrialCreator field_trial_creator( + &prefs_, &variations_service_client, &safe_seed_manager); + + const base::Version& current_version = version_info::GetVersion(); + EXPECT_TRUE(current_version.IsValid()); + + std::unique_ptr<ClientFilterableState> client_filterable_state = + field_trial_creator.GetClientFilterableStateForVersion(current_version); + EXPECT_NE(client_filterable_state->hardware_class, std::string()); +} #endif // OS_ANDROID } // namespace variations diff --git a/chromium/components/viz/common/features.cc b/chromium/components/viz/common/features.cc index abab71621e5..b7a29046cac 100644 --- a/chromium/components/viz/common/features.cc +++ b/chromium/components/viz/common/features.cc @@ -22,7 +22,7 @@ const base::Feature kEnableSurfaceSynchronization{ // (OOP-D). // TODO(dnicoara): Look at enabling Chromecast support when ChromeOS support is // ready. -#if defined(IS_CHROMECAST) +#if defined(IS_CHROMECAST) || defined(OS_CHROMEOS) const base::Feature kVizDisplayCompositor{"VizDisplayCompositor", base::FEATURE_DISABLED_BY_DEFAULT}; #else diff --git a/chromium/components/viz/common/quads/shared_quad_state.h b/chromium/components/viz/common/quads/shared_quad_state.h index b64893faef9..647fd47047e 100644 --- a/chromium/components/viz/common/quads/shared_quad_state.h +++ b/chromium/components/viz/common/quads/shared_quad_state.h @@ -66,9 +66,9 @@ class VIZ_COMMON_EXPORT SharedQuadState { float opacity; SkBlendMode blend_mode; int sorting_context_id; - // Used by SurfaceAggregator to decide whether to merge quads for a surface - // into their target render pass. It is a performance optimization by avoiding - // render passes as much as possible. + // An internal flag used only by the SurfaceAggregator to decide whether to + // merge quads for a surface into their target render pass. It is a + // performance optimization by avoiding render passes as much as possible. bool is_fast_rounded_corner = false; // This is for underlay optimization and used only in the SurfaceAggregator // and the OverlayProcessor. This damage rect contains union of damage from diff --git a/chromium/content/browser/BUILD.gn b/chromium/content/browser/BUILD.gn index aff5de7521d..32fa9e47d47 100644 --- a/chromium/content/browser/BUILD.gn +++ b/chromium/content/browser/BUILD.gn @@ -184,6 +184,7 @@ jumbo_source_set("browser") { "//services/tracing/public/cpp", "//services/video_capture:lib", "//services/video_capture/public/cpp", + "//services/video_capture/public/cpp:manifest", "//services/video_capture/public/mojom:constants", "//services/video_capture/public/uma", "//services/viz/privileged/interfaces", @@ -1892,7 +1893,6 @@ jumbo_source_set("browser") { "url_loader_factory_getter.h", "utility_process_host.cc", "utility_process_host.h", - "video_capture_service.cc", "wake_lock/wake_lock_context_host.cc", "wake_lock/wake_lock_context_host.h", "wake_lock/wake_lock_service_impl.cc", diff --git a/chromium/content/browser/accessibility/browser_accessibility.h b/chromium/content/browser/accessibility/browser_accessibility.h index 7016c3f3e7d..a1d4afc6aa1 100644 --- a/chromium/content/browser/accessibility/browser_accessibility.h +++ b/chromium/content/browser/accessibility/browser_accessibility.h @@ -91,8 +91,6 @@ class CONTENT_EXPORT BrowserAccessibility : public ui::AXPlatformNodeDelegate { // its data changes. virtual void OnDataChanged() {} - virtual void OnSubtreeWillBeDeleted() {} - // Called when the location changed. virtual void OnLocationChanged() {} diff --git a/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm b/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm index a2927a31d6b..4889a5c2f5d 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm @@ -38,6 +38,8 @@ using BrowserAccessibilityPositionInstance = content::BrowserAccessibilityPosition::AXPositionInstance; +using SerializedPosition = + content::BrowserAccessibilityPosition::SerializedPosition; using AXPlatformRange = ui::AXRange<BrowserAccessibilityPositionInstance::element_type>; using AXTextMarkerRangeRef = CFTypeRef; @@ -54,6 +56,10 @@ using content::OneShotAccessibilityTreeSearch; using ui::AXNodeData; using ui::AXTreeIDRegistry; +static_assert( + std::is_trivially_copyable<SerializedPosition>::value, + "SerializedPosition must be POD because it's used to back an AXTextMarker"); + namespace { // Private WebKit accessibility attributes. @@ -172,21 +178,24 @@ AXTextMarkerRef AXTextMarkerRangeCopyEndMarker( // AXTextMarkerCreate copies from data buffer given to it. id CreateTextMarker(BrowserAccessibilityPositionInstance position) { + SerializedPosition serialized = position->Serialize(); AXTextMarkerRef text_marker = AXTextMarkerCreate( - kCFAllocatorDefault, reinterpret_cast<const UInt8*>(position.get()), - sizeof(BrowserAccessibilityPosition)); + kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized), + sizeof(SerializedPosition)); return [static_cast<id>(text_marker) autorelease]; } // |range| is destructed at the end of this method. |anchor| and |focus| are // copied into the individual text markers. id CreateTextMarkerRange(const AXPlatformRange range) { + SerializedPosition serialized_anchor = range.anchor()->Serialize(); + SerializedPosition serialized_focus = range.focus()->Serialize(); base::ScopedCFTypeRef<AXTextMarkerRef> start_marker(AXTextMarkerCreate( - kCFAllocatorDefault, reinterpret_cast<const UInt8*>(range.anchor()), - sizeof(BrowserAccessibilityPosition))); + kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized_anchor), + sizeof(SerializedPosition))); base::ScopedCFTypeRef<AXTextMarkerRef> end_marker(AXTextMarkerCreate( - kCFAllocatorDefault, reinterpret_cast<const UInt8*>(range.focus()), - sizeof(BrowserAccessibilityPosition))); + kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized_focus), + sizeof(SerializedPosition))); AXTextMarkerRangeRef marker_range = AXTextMarkerRangeCreate(kCFAllocatorDefault, start_marker, end_marker); return [static_cast<id>(marker_range) autorelease]; @@ -195,22 +204,15 @@ id CreateTextMarkerRange(const AXPlatformRange range) { BrowserAccessibilityPositionInstance CreatePositionFromTextMarker( AXTextMarkerRef text_marker) { DCHECK(text_marker); - if (AXTextMarkerGetLength(text_marker) != - sizeof(BrowserAccessibilityPosition)) + if (AXTextMarkerGetLength(text_marker) != sizeof(SerializedPosition)) return BrowserAccessibilityPosition::CreateNullPosition(); + const UInt8* source_buffer = AXTextMarkerGetBytePtr(text_marker); if (!source_buffer) return BrowserAccessibilityPosition::CreateNullPosition(); - UInt8* destination_buffer = new UInt8[sizeof(BrowserAccessibilityPosition)]; - std::memcpy(destination_buffer, source_buffer, - sizeof(BrowserAccessibilityPosition)); - BrowserAccessibilityPosition::AXPositionInstance position( - reinterpret_cast< - BrowserAccessibilityPosition::AXPositionInstance::pointer>( - destination_buffer)); - if (!position) - return BrowserAccessibilityPosition::CreateNullPosition(); - return position; + + return BrowserAccessibilityPosition::Unserialize( + *reinterpret_cast<const SerializedPosition*>(source_buffer)); } AXPlatformRange CreateRangeFromTextMarkerRange( diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager.cc b/chromium/content/browser/accessibility/browser_accessibility_manager.cc index 1e07bc2e39c..7ffe23034a6 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager.cc @@ -1213,11 +1213,7 @@ void BrowserAccessibilityManager::OnNodeWillBeDeleted(ui::AXTree* tree, } void BrowserAccessibilityManager::OnSubtreeWillBeDeleted(ui::AXTree* tree, - ui::AXNode* node) { - DCHECK(node); - if (BrowserAccessibility* wrapper = GetFromAXNode(node)) - wrapper->OnSubtreeWillBeDeleted(); -} + ui::AXNode* node) {} void BrowserAccessibilityManager::OnNodeCreated(ui::AXTree* tree, ui::AXNode* node) { diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.cc index ef4b81e43f4..428610edbec 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.cc @@ -234,20 +234,20 @@ static void EstablishEmbeddedRelationship(AtkObject* document_object) { document_platform_node->SetEmbeddingWindow(window); } -void BrowserAccessibilityManagerAuraLinux::OnStateChanged( +void BrowserAccessibilityManagerAuraLinux::OnNodeDataWillChange( ui::AXTree* tree, - ui::AXNode* node, - ax::mojom::State state, - bool new_value) { + const ui::AXNodeData& old_node_data, + const ui::AXNodeData& new_node_data) { DCHECK_EQ(ax_tree(), tree); // Since AuraLinux needs to send the children-changed::remove event with the // index in parent, the event must be fired before the node becomes ignored. // children-changed:add is handled with the generated Event::IGNORED_CHANGED. - if (state == ax::mojom::State::kIgnored && new_value) { - DCHECK(!node->data().HasState(ax::mojom::State::kIgnored)); - BrowserAccessibility* obj = GetFromAXNode(node); + if (!old_node_data.HasState(ax::mojom::State::kIgnored) && + new_node_data.HasState(ax::mojom::State::kIgnored)) { + BrowserAccessibility* obj = GetFromID(old_node_data.id); if (obj && obj->IsNative() && obj->GetParent()) { + DCHECK(!obj->HasState(ax::mojom::State::kIgnored)); g_signal_emit_by_name(obj->GetParent(), "children-changed::remove", obj->GetIndexInParent(), obj->GetNativeViewAccessible()); @@ -258,7 +258,6 @@ void BrowserAccessibilityManagerAuraLinux::OnStateChanged( void BrowserAccessibilityManagerAuraLinux::OnSubtreeWillBeDeleted( ui::AXTree* tree, ui::AXNode* node) { - BrowserAccessibilityManager::OnSubtreeWillBeDeleted(tree, node); // Sending events on load/destruction would create a lot of spam, avoid that. if (!GetTreeData().loaded) return; diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.h b/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.h index 4f002468799..3826fd5334c 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.h @@ -42,10 +42,9 @@ class CONTENT_EXPORT BrowserAccessibilityManagerAuraLinux protected: // AXTreeObserver methods. - void OnStateChanged(ui::AXTree* tree, - ui::AXNode* node, - ax::mojom::State state, - bool new_value) override; + void OnNodeDataWillChange(ui::AXTree* tree, + const ui::AXNodeData& old_node_data, + const ui::AXNodeData& new_node_data) override; void OnSubtreeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override; void OnAtomicUpdateFinished( ui::AXTree* tree, diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc index d444d88d857..7bbfed5a507 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc @@ -75,19 +75,6 @@ HWND BrowserAccessibilityManagerWin::GetParentHWND() { return delegate->AccessibilityGetAcceleratedWidget(); } -void BrowserAccessibilityManagerWin::OnSubtreeWillBeDeleted(ui::AXTree* tree, - ui::AXNode* node) { - BrowserAccessibilityManager::OnSubtreeWillBeDeleted(tree, node); - - BrowserAccessibility* obj = GetFromAXNode(node); - FireWinAccessibilityEvent(EVENT_OBJECT_HIDE, obj); - FireUiaStructureChangedEvent(StructureChangeType_ChildRemoved, obj); - if (obj && obj->GetRole() == ax::mojom::Role::kMenu) { - FireWinAccessibilityEvent(EVENT_SYSTEM_MENUPOPUPEND, obj); - FireUiaAccessibilityEvent(UIA_MenuClosedEventId, obj); - } -} - void BrowserAccessibilityManagerWin::UserIsReloading() { if (GetRoot()) FireWinAccessibilityEvent(IA2_EVENT_DOCUMENT_RELOAD, GetRoot()); @@ -576,6 +563,20 @@ gfx::Rect BrowserAccessibilityManagerWin::GetViewBounds() { return gfx::Rect(); } +void BrowserAccessibilityManagerWin::OnSubtreeWillBeDeleted(ui::AXTree* tree, + ui::AXNode* node) { + BrowserAccessibility* obj = GetFromAXNode(node); + DCHECK(obj); + if (obj) { + FireWinAccessibilityEvent(EVENT_OBJECT_HIDE, obj); + FireUiaStructureChangedEvent(StructureChangeType_ChildRemoved, obj); + if (obj->GetRole() == ax::mojom::Role::kMenu) { + FireWinAccessibilityEvent(EVENT_SYSTEM_MENUPOPUPEND, obj); + FireUiaAccessibilityEvent(UIA_MenuClosedEventId, obj); + } + } +} + void BrowserAccessibilityManagerWin::OnAtomicUpdateFinished( ui::AXTree* tree, bool root_changed, diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_win.h b/chromium/content/browser/accessibility/browser_accessibility_manager_win.h index bb2457badef..9b8a9387207 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_win.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_win.h @@ -43,9 +43,6 @@ class CONTENT_EXPORT BrowserAccessibilityManagerWin // Get the closest containing HWND. HWND GetParentHWND(); - // AXEventGenerator methods - void OnSubtreeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override; - // BrowserAccessibilityManager methods void UserIsReloading() override; BrowserAccessibility* GetFocus() const override; @@ -82,6 +79,7 @@ class CONTENT_EXPORT BrowserAccessibilityManagerWin protected: // AXTreeObserver methods. + void OnSubtreeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override; void OnAtomicUpdateFinished( ui::AXTree* tree, bool root_changed, diff --git a/chromium/content/browser/accessibility/browser_accessibility_unittest.cc b/chromium/content/browser/accessibility/browser_accessibility_unittest.cc index bab809c51d1..eff14f455d4 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_unittest.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_unittest.cc @@ -210,63 +210,63 @@ TEST_F(BrowserAccessibilityTest, TestGetDescendants) { TEST_F(BrowserAccessibilityTest, PlatformChildIterator) { // (i) => node is ignored - // 0 + // 1 // |__________ // | | | - // 1(i) 2 3 + // 2(i) 3 4 // |_______________________ // | | | | - // 4 5 6(i) 7(i) + // 5 6 7(i) 8(i) // | | |________ // | | | | - // 8 9(i) 10(i) 11 + // 9 10(i) 11(i) 12 // | |____ // | | | - // 12(i) 13 14 + // 13(i) 14 15 ui::AXTreeUpdate tree_update; - tree_update.root_id = 0; + tree_update.root_id = 1; tree_update.nodes.resize(15); - tree_update.nodes[0].id = 0; - tree_update.nodes[0].child_ids = {1, 2, 3}; + tree_update.nodes[0].id = 1; + tree_update.nodes[0].child_ids = {2, 3, 4}; - tree_update.nodes[1].id = 1; - tree_update.nodes[1].child_ids = {4, 5, 6, 7}; + tree_update.nodes[1].id = 2; + tree_update.nodes[1].child_ids = {5, 6, 7, 8}; tree_update.nodes[1].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[2].id = 2; - tree_update.nodes[3].id = 3; + tree_update.nodes[2].id = 3; + tree_update.nodes[3].id = 4; - tree_update.nodes[4].id = 4; - tree_update.nodes[4].child_ids = {8}; + tree_update.nodes[4].id = 5; + tree_update.nodes[4].child_ids = {9}; - tree_update.nodes[5].id = 5; - tree_update.nodes[5].child_ids = {9}; + tree_update.nodes[5].id = 6; + tree_update.nodes[5].child_ids = {10}; - tree_update.nodes[6].id = 6; - tree_update.nodes[6].child_ids = {10, 11}; + tree_update.nodes[6].id = 7; + tree_update.nodes[6].child_ids = {11, 12}; tree_update.nodes[6].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[7].id = 7; + tree_update.nodes[7].id = 8; tree_update.nodes[7].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[8].id = 8; + tree_update.nodes[8].id = 9; - tree_update.nodes[9].id = 9; - tree_update.nodes[9].child_ids = {12}; + tree_update.nodes[9].id = 10; + tree_update.nodes[9].child_ids = {13}; tree_update.nodes[9].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[10].id = 10; - tree_update.nodes[10].child_ids = {13, 14}; + tree_update.nodes[10].id = 11; + tree_update.nodes[10].child_ids = {14, 15}; tree_update.nodes[10].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[11].id = 11; + tree_update.nodes[11].id = 12; - tree_update.nodes[12].id = 12; + tree_update.nodes[12].id = 13; tree_update.nodes[12].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[13].id = 13; + tree_update.nodes[13].id = 14; - tree_update.nodes[14].id = 14; + tree_update.nodes[14].id = 15; std::unique_ptr<BrowserAccessibilityManager> manager( BrowserAccessibilityManager::Create( @@ -275,61 +275,61 @@ TEST_F(BrowserAccessibilityTest, PlatformChildIterator) { BrowserAccessibility* root_obj = manager->GetRoot(); // Test traversal - // PlatformChildren(root_obj) = {4, 5, 13, 14, 11, 2, 3} + // PlatformChildren(root_obj) = {5, 6, 14, 15, 12, 3, 4} BrowserAccessibility::PlatformChildIterator platform_iterator = root_obj->PlatformChildrenBegin(); - EXPECT_EQ(4, platform_iterator->GetId()); - - ++platform_iterator; EXPECT_EQ(5, platform_iterator->GetId()); ++platform_iterator; - EXPECT_EQ(13, platform_iterator->GetId()); + EXPECT_EQ(6, platform_iterator->GetId()); ++platform_iterator; EXPECT_EQ(14, platform_iterator->GetId()); - --platform_iterator; - EXPECT_EQ(13, platform_iterator->GetId()); + ++platform_iterator; + EXPECT_EQ(15, platform_iterator->GetId()); --platform_iterator; - EXPECT_EQ(5, platform_iterator->GetId()); + EXPECT_EQ(14, platform_iterator->GetId()); - ++platform_iterator; - EXPECT_EQ(13, platform_iterator->GetId()); + --platform_iterator; + EXPECT_EQ(6, platform_iterator->GetId()); ++platform_iterator; EXPECT_EQ(14, platform_iterator->GetId()); ++platform_iterator; - EXPECT_EQ(11, platform_iterator->GetId()); + EXPECT_EQ(15, platform_iterator->GetId()); ++platform_iterator; - EXPECT_EQ(2, platform_iterator->GetId()); + EXPECT_EQ(12, platform_iterator->GetId()); ++platform_iterator; EXPECT_EQ(3, platform_iterator->GetId()); ++platform_iterator; + EXPECT_EQ(4, platform_iterator->GetId()); + + ++platform_iterator; EXPECT_EQ(root_obj->PlatformChildrenEnd(), platform_iterator); // test empty list // PlatformChildren(2) = {} - BrowserAccessibility* node2 = manager->GetFromID(2); + BrowserAccessibility* node2 = manager->GetFromID(3); platform_iterator = node2->PlatformChildrenBegin(); EXPECT_EQ(node2->PlatformChildrenEnd(), platform_iterator); // empty list from ignored node // PlatformChildren(7) = {} - BrowserAccessibility* node7 = manager->GetFromID(7); + BrowserAccessibility* node7 = manager->GetFromID(8); platform_iterator = node7->PlatformChildrenBegin(); EXPECT_EQ(node7->PlatformChildrenEnd(), platform_iterator); // non-empty list from ignored node - // PlatformChildren(10) = {13, 14} - BrowserAccessibility* node10 = manager->GetFromID(10); + // PlatformChildren(10) = {14, 15} + BrowserAccessibility* node10 = manager->GetFromID(11); platform_iterator = node10->PlatformChildrenBegin(); - EXPECT_EQ(13, platform_iterator->GetId()); + EXPECT_EQ(14, platform_iterator->GetId()); // Two UnignoredChildIterators from the same parent at the same position // should be equivalent, even in end position. diff --git a/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc index db623358e04..3ed0354ecca 100644 --- a/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc +++ b/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc @@ -2043,6 +2043,10 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, RunHtmlTest(FILE_PATH_LITERAL("button-with-listbox-popup.html")); } +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, DeleteSelectionCrash) { + RunHtmlTest(FILE_PATH_LITERAL("delete-selection-crash.html")); +} + // // Regression tests. These don't test a specific web platform feature, // they test a specific web page that crashed or had some bad behavior diff --git a/chromium/content/browser/builtin_service_manifests.cc b/chromium/content/browser/builtin_service_manifests.cc index 5fb43e95ba5..07e202925d5 100644 --- a/chromium/content/browser/builtin_service_manifests.cc +++ b/chromium/content/browser/builtin_service_manifests.cc @@ -30,6 +30,7 @@ #include "services/service_manager/public/cpp/manifest_builder.h" #include "services/shape_detection/public/cpp/manifest.h" #include "services/tracing/manifest.h" +#include "services/video_capture/public/cpp/manifest.h" #if defined(OS_LINUX) #include "components/services/font/public/cpp/manifest.h" // nogncheck @@ -90,6 +91,12 @@ const std::vector<service_manager::Manifest>& GetBuiltinServiceManifests() { resource_coordinator::GetManifest(), shape_detection::GetManifest(), tracing::GetManifest(), + video_capture::GetManifest( + features::IsVideoCaptureServiceEnabledForOutOfProcess() + ? service_manager::Manifest::ExecutionMode:: + kOutOfProcessBuiltin + : service_manager::Manifest::ExecutionMode:: + kInProcessBuiltin), #if defined(OS_LINUX) font_service::GetManifest(), #endif diff --git a/chromium/content/browser/download/download_browsertest.cc b/chromium/content/browser/download/download_browsertest.cc index ac10788e493..50ca50ae5c6 100644 --- a/chromium/content/browser/download/download_browsertest.cc +++ b/chromium/content/browser/download/download_browsertest.cc @@ -623,6 +623,38 @@ class ErrorStreamCountingObserver : download::DownloadItem::Observer { base::Closure completion_closure_; }; +// Class to wait for a WebContents to kick off a specified number of +// navigations. +class NavigationStartObserver : public WebContentsObserver { + public: + explicit NavigationStartObserver(WebContents* web_contents) + : WebContentsObserver(web_contents) {} + ~NavigationStartObserver() override {} + + void WaitForFinished(int navigation_count) { + if (start_count_ >= navigation_count) + return; + navigation_count_ = navigation_count; + base::RunLoop run_loop; + completion_closure_ = run_loop.QuitClosure(); + run_loop.Run(); + } + + private: + // WebContentsObserver implementations. + void DidStartNavigation(NavigationHandle* navigation_handle) override { + start_count_++; + if (start_count_ >= navigation_count_ && !completion_closure_.is_null()) { + std::move(completion_closure_).Run(); + } + } + + int navigation_count_ = 0; + int start_count_ = 0; + base::Closure completion_closure_; + DISALLOW_COPY_AND_ASSIGN(NavigationStartObserver); +}; + bool IsDownloadInState(download::DownloadItem::DownloadState state, download::DownloadItem* item) { return item->GetState() == state; @@ -3356,8 +3388,54 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, std::vector<download::DownloadItem*> downloads; DownloadManagerForShell(shell())->GetAllDownloads(&downloads); ASSERT_EQ(0u, downloads.size()); + ASSERT_TRUE(origin_one.ShutdownAndWaitUntilComplete()); + ASSERT_TRUE(origin_two.ShutdownAndWaitUntilComplete()); +} + +// Tests that if a renderer initiated download triggers cross origin in the +// redirect chain, the visible URL of the current tab shouldn't change. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, + DownloadAttributeSameOriginRedirectNavigationTimeOut) { + net::EmbeddedTestServer origin_one; + net::EmbeddedTestServer origin_two; + ASSERT_TRUE(origin_one.InitializeAndListen()); + ASSERT_TRUE(origin_two.InitializeAndListen()); + // The download-attribute.html page contains an anchor element whose href is + // set to the value of the query parameter (specified as |target| in the URL + // below). The suggested filename for the anchor is 'suggested-filename'. When + // the page is loaded, a script simulates a click on the anchor, triggering a + // download of the target URL. + // + // We construct two test servers; origin_one and origin_two. Once started, the + // server URLs will differ by the port number. Therefore they will be in + // different origins. + GURL download_url = origin_one.GetURL("/ping"); + GURL referrer_url = origin_one.GetURL( + std::string("/download-attribute.html?target=") + download_url.spec()); + origin_one.ServeFilesFromDirectory(GetTestFilePath("download", "")); + + // <origin_one>/download-attribute.html initiates a download of + // <origin_one>/ping, which redirects to <origin_two>/download. The latter + // will time out. + origin_one.RegisterRequestHandler( + CreateRedirectHandler("/ping", origin_two.GetURL("/download"))); + + origin_one.StartAcceptingConnections(); + + NavigationStartObserver obs(shell()->web_contents()); + NavigationController::LoadURLParams params(referrer_url); + params.transition_type = ui::PageTransitionFromInt( + ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR); + shell()->web_contents()->GetController().LoadURLWithParams(params); + shell()->web_contents()->Focus(); + + // Waiting for 2 navigation to happen, one for the original request, one for + // the redirect. + obs.WaitForFinished(2); + EXPECT_EQ(referrer_url, shell()->web_contents()->GetVisibleURL()); ASSERT_TRUE(origin_one.ShutdownAndWaitUntilComplete()); + origin_two.StartAcceptingConnections(); ASSERT_TRUE(origin_two.ShutdownAndWaitUntilComplete()); } diff --git a/chromium/content/browser/download/download_manager_impl.cc b/chromium/content/browser/download/download_manager_impl.cc index 2d597182720..624af7658eb 100644 --- a/chromium/content/browser/download/download_manager_impl.cc +++ b/chromium/content/browser/download/download_manager_impl.cc @@ -556,6 +556,7 @@ bool DownloadManagerImpl::InterceptDownload( info.render_process_id, info.render_frame_id); params.from_download_cross_origin_redirect = true; params.initiator_origin = info.request_initiator; + params.is_renderer_initiated = info.is_content_initiated; web_contents->GetController().LoadURLWithParams(params); } if (info.request_handle) diff --git a/chromium/content/browser/frame_host/navigation_handle_impl.cc b/chromium/content/browser/frame_host/navigation_handle_impl.cc index 50ee263651f..3b11c0e1abe 100644 --- a/chromium/content/browser/frame_host/navigation_handle_impl.cc +++ b/chromium/content/browser/frame_host/navigation_handle_impl.cc @@ -450,6 +450,7 @@ void NavigationHandleImpl::InitServiceWorkerHandle( } void NavigationHandleImpl::RenderProcessBlockedStateChanged(bool blocked) { + AddNetworkServiceDebugEvent(std::string("B") + (blocked ? "1" : "0")); if (blocked) StopCommitTimeout(); else @@ -484,6 +485,7 @@ void NavigationHandleImpl::RestartCommitTimeout() { void NavigationHandleImpl::OnCommitTimeout() { DCHECK_EQ(NavigationRequest::READY_TO_COMMIT, state()); + AddNetworkServiceDebugEvent("T"); #if defined(OS_ANDROID) // Rate limit the number of stack dumps so we don't overwhelm our crash // reports. @@ -510,6 +512,12 @@ void NavigationHandleImpl::OnCommitTimeout() { base::debug::ScopedCrashKeyString scoped_memory( memory_key, base::NumberToString(base::SysInfo::AmountOfPhysicalMemoryMB())); + + static base::debug::CrashKeyString* debug_string_key = + base::debug::AllocateCrashKeyString("ns_debug_events", + base::debug::CrashKeySize::Size256); + base::debug::ScopedCrashKeyString scoped_debug_string( + debug_string_key, GetNetworkServiceDebugEventsString()); base::debug::DumpWithoutCrashing(); if (IsOutOfProcessNetworkService()) diff --git a/chromium/content/browser/frame_host/navigation_request.cc b/chromium/content/browser/frame_host/navigation_request.cc index 4bb7b470e7f..8175ccc7dc7 100644 --- a/chromium/content/browser/frame_host/navigation_request.cc +++ b/chromium/content/browser/frame_host/navigation_request.cc @@ -2048,6 +2048,8 @@ void NavigationRequest::CommitNavigation() { commit_params_.prefetched_signed_exchanges = std::move(subresource_loader_params_->prefetched_signed_exchanges); } + + AddNetworkServiceDebugEvent("COM"); render_frame_host_->CommitNavigation( this, common_params_, commit_params_, response_head_.get(), std::move(response_body_), std::move(url_loader_client_endpoints_), @@ -2107,6 +2109,16 @@ void NavigationRequest::RenderProcessHostDestroyed(RenderProcessHost* host) { ResetExpectedProcess(); } +void NavigationRequest::RenderProcessReady(RenderProcessHost* host) { + AddNetworkServiceDebugEvent("RPR"); +} + +void NavigationRequest::RenderProcessExited( + RenderProcessHost* host, + const ChildProcessTerminationInfo& info) { + AddNetworkServiceDebugEvent("RPE"); +} + void NavigationRequest::UpdateSiteURL( RenderProcessHost* post_redirect_process) { GURL new_site_url = GetSiteForCommonParamsURL(); @@ -2729,6 +2741,7 @@ void NavigationRequest::DidCommitNavigation( bool did_replace_entry, const GURL& previous_url, NavigationType navigation_type) { + AddNetworkServiceDebugEvent("DCN"); common_params_.url = params.url; did_replace_entry_ = did_replace_entry; should_update_history_ = params.should_update_history; @@ -2847,6 +2860,9 @@ void NavigationRequest::ReadyToCommitNavigation(bool is_error) { TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationHandle", this, "ReadyToCommitNavigation"); + AddNetworkServiceDebugEvent( + std::string("RTCN") + + (render_frame_host_->GetProcess()->IsReady() ? "1" : "0")); handle_state_ = READY_TO_COMMIT; ready_to_commit_time_ = base::TimeTicks::Now(); navigation_handle_->RestartCommitTimeout(); diff --git a/chromium/content/browser/frame_host/navigation_request.h b/chromium/content/browser/frame_host/navigation_request.h index 67c95139821..92c2c4fba60 100644 --- a/chromium/content/browser/frame_host/navigation_request.h +++ b/chromium/content/browser/frame_host/navigation_request.h @@ -19,6 +19,7 @@ #include "content/browser/initiator_csp_context.h" #include "content/browser/loader/navigation_url_loader_delegate.h" #include "content/browser/navigation_subresource_loader_params.h" +#include "content/browser/network_service_instance_impl.h" #include "content/browser/web_package/bundled_exchanges_factory.h" #include "content/common/content_export.h" #include "content/common/frame_message_enums.h" @@ -688,6 +689,9 @@ class CONTENT_EXPORT NavigationRequest : public NavigationURLLoaderDelegate, bool IsSelfReferentialURL(); // RenderProcessHostObserver implementation. + void RenderProcessReady(RenderProcessHost* host) override; + void RenderProcessExited(RenderProcessHost* host, + const ChildProcessTerminationInfo& info) override; void RenderProcessHostDestroyed(RenderProcessHost* host) override; void RecordNavigationMetrics() const; diff --git a/chromium/content/browser/network_service_instance_impl.cc b/chromium/content/browser/network_service_instance_impl.cc index 4c8203f4a97..53832bbbfe7 100644 --- a/chromium/content/browser/network_service_instance_impl.cc +++ b/chromium/content/browser/network_service_instance_impl.cc @@ -54,6 +54,12 @@ network::NetworkConnectionTracker* g_network_connection_tracker; bool g_network_service_is_responding = false; base::Time g_last_network_service_crash; +std::deque<std::pair<std::string, base::Time>>& GetDebugEvents() { + static base::NoDestructor<std::deque<std::pair<std::string, base::Time>>> + debug_events; + return *debug_events; +} + std::unique_ptr<network::NetworkService>& GetLocalNetworkService() { static base::NoDestructor< base::SequenceLocalStorageSlot<std::unique_ptr<network::NetworkService>>> @@ -120,6 +126,7 @@ void OnNetworkServiceCrash() { DCHECK(g_network_service_ptr->encountered_error()); g_last_network_service_crash = base::Time::Now(); GetCrashHandlersList().Notify(); + AddNetworkServiceDebugEvent("ONSC"); } // Parses the desired granularity of NetLog capturing specified by the command @@ -206,6 +213,7 @@ CONTENT_EXPORT network::mojom::NetworkService* GetNetworkServiceFromConnector( mojo::MakeRequest(g_network_service_ptr))); } + AddNetworkServiceDebugEvent("START"); network::mojom::NetworkServiceClientPtr client_ptr; auto client_request = mojo::MakeRequest(&client_ptr); // Call SetClient before creating NetworkServiceClient, as the latter @@ -215,6 +223,7 @@ CONTENT_EXPORT network::mojom::NetworkService* GetNetworkServiceFromConnector( g_network_service_is_responding = false; g_network_service_ptr->QueryVersion(base::BindRepeating( [](base::Time start_time, uint32_t) { + AddNetworkServiceDebugEvent("RESP"); g_network_service_is_responding = true; base::TimeDelta delta = base::Time::Now() - start_time; UMA_HISTOGRAM_MEDIUM_TIMES("NetworkService.TimeToFirstResponse", @@ -411,4 +420,24 @@ void PingNetworkService(base::OnceClosure closure) { base::Passed(std::move(closure)))); } +void AddNetworkServiceDebugEvent(const std::string& event) { + auto& events = GetDebugEvents(); + events.push_front({event, base::Time::Now()}); + // Keep at most 20 most recent events. + if (events.size() > 20) + events.pop_back(); +} + +std::string GetNetworkServiceDebugEventsString() { + auto& events = GetDebugEvents(); + if (events.empty()) + return std::string(); + std::stringstream stream; + base::Time now = base::Time::Now(); + for (const auto& info : events) { + stream << info.first << ":" << (now - info.second).InSecondsF() << ","; + } + return stream.str(); +} + } // namespace content diff --git a/chromium/content/browser/network_service_instance_impl.h b/chromium/content/browser/network_service_instance_impl.h index f3c93c1bea4..0c401f8d9a5 100644 --- a/chromium/content/browser/network_service_instance_impl.h +++ b/chromium/content/browser/network_service_instance_impl.h @@ -44,6 +44,8 @@ enum class NetworkServiceAvailability { CONTENT_EXPORT NetworkServiceAvailability GetNetworkServiceAvailability(); CONTENT_EXPORT base::TimeDelta GetTimeSinceLastNetworkServiceCrash(); CONTENT_EXPORT void PingNetworkService(base::OnceClosure closure); +CONTENT_EXPORT void AddNetworkServiceDebugEvent(const std::string& event); +CONTENT_EXPORT std::string GetNetworkServiceDebugEventsString(); } // namespace content diff --git a/chromium/content/browser/renderer_host/media/media_stream_manager.cc b/chromium/content/browser/renderer_host/media/media_stream_manager.cc index 8a61f6c8e00..61ec39f62d4 100644 --- a/chromium/content/browser/renderer_host/media/media_stream_manager.cc +++ b/chromium/content/browser/renderer_host/media/media_stream_manager.cc @@ -548,6 +548,7 @@ MediaStreamManager::MediaStreamManager( if (base::FeatureList::IsEnabled(features::kMojoVideoCapture)) { video_capture_provider = std::make_unique<VideoCaptureProviderSwitcher>( std::make_unique<ServiceVideoCaptureProvider>( + GetSystemConnector(), base::BindRepeating(&SendVideoCaptureLogMessage)), InProcessVideoCaptureProvider::CreateInstanceForNonDeviceCapture( std::move(device_task_runner), diff --git a/chromium/content/browser/renderer_host/media/ref_counted_video_source_provider.cc b/chromium/content/browser/renderer_host/media/ref_counted_video_source_provider.cc index 5d5e0dca013..b02fcc94cc6 100644 --- a/chromium/content/browser/renderer_host/media/ref_counted_video_source_provider.cc +++ b/chromium/content/browser/renderer_host/media/ref_counted_video_source_provider.cc @@ -4,14 +4,14 @@ #include "content/browser/renderer_host/media/ref_counted_video_source_provider.h" -#include "content/public/browser/video_capture_service.h" - namespace content { RefCountedVideoSourceProvider::RefCountedVideoSourceProvider( video_capture::mojom::VideoSourceProviderPtr source_provider, + video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider, base::OnceClosure destruction_cb) : source_provider_(std::move(source_provider)), + device_factory_provider_(std::move(device_factory_provider)), destruction_cb_(std::move(destruction_cb)) {} RefCountedVideoSourceProvider::~RefCountedVideoSourceProvider() { @@ -23,8 +23,12 @@ RefCountedVideoSourceProvider::GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } +void RefCountedVideoSourceProvider::ShutdownServiceAsap() { + device_factory_provider_->ShutdownServiceAsap(); +} + void RefCountedVideoSourceProvider::SetRetryCount(int32_t count) { - GetVideoCaptureService().SetRetryCount(count); + device_factory_provider_->SetRetryCount(count); } void RefCountedVideoSourceProvider::ReleaseProviderForTesting() { diff --git a/chromium/content/browser/renderer_host/media/ref_counted_video_source_provider.h b/chromium/content/browser/renderer_host/media/ref_counted_video_source_provider.h index d1b8eacfbe6..105843f3406 100644 --- a/chromium/content/browser/renderer_host/media/ref_counted_video_source_provider.h +++ b/chromium/content/browser/renderer_host/media/ref_counted_video_source_provider.h @@ -7,6 +7,7 @@ #include "base/memory/ref_counted.h" #include "content/common/content_export.h" +#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" #include "services/video_capture/public/mojom/video_source_provider.mojom.h" namespace content { @@ -21,6 +22,7 @@ class CONTENT_EXPORT RefCountedVideoSourceProvider public: RefCountedVideoSourceProvider( video_capture::mojom::VideoSourceProviderPtr source_provider, + video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider, base::OnceClosure destruction_cb); base::WeakPtr<RefCountedVideoSourceProvider> GetWeakPtr(); @@ -29,6 +31,7 @@ class CONTENT_EXPORT RefCountedVideoSourceProvider return source_provider_; } + void ShutdownServiceAsap(); void SetRetryCount(int32_t count); void ReleaseProviderForTesting(); @@ -37,6 +40,7 @@ class CONTENT_EXPORT RefCountedVideoSourceProvider ~RefCountedVideoSourceProvider(); video_capture::mojom::VideoSourceProviderPtr source_provider_; + video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider_; base::OnceClosure destruction_cb_; base::WeakPtrFactory<RefCountedVideoSourceProvider> weak_ptr_factory_{this}; diff --git a/chromium/content/browser/renderer_host/media/service_launched_video_capture_device.h b/chromium/content/browser/renderer_host/media/service_launched_video_capture_device.h index 4f05f15d273..b6a605e5059 100644 --- a/chromium/content/browser/renderer_host/media/service_launched_video_capture_device.h +++ b/chromium/content/browser/renderer_host/media/service_launched_video_capture_device.h @@ -11,8 +11,8 @@ namespace content { -// Implementation of LaunchedVideoCaptureDevice that uses -// video_capture::mojom::VideoCaptureService. +// Implementation of LaunchedVideoCaptureDevice that uses the "video_capture" +// service. class ServiceLaunchedVideoCaptureDevice : public LaunchedVideoCaptureDevice { public: ServiceLaunchedVideoCaptureDevice( diff --git a/chromium/content/browser/renderer_host/media/service_video_capture_device_launcher.h b/chromium/content/browser/renderer_host/media/service_video_capture_device_launcher.h index c080c1a909b..7e87e6463bb 100644 --- a/chromium/content/browser/renderer_host/media/service_video_capture_device_launcher.h +++ b/chromium/content/browser/renderer_host/media/service_video_capture_device_launcher.h @@ -13,8 +13,8 @@ namespace content { -// Implementation of VideoCaptureDeviceLauncher that uses uses -// video_capture::mojom::VideoCaptureService. +// Implementation of VideoCaptureDeviceLauncher that uses the "video_capture" +// service. class CONTENT_EXPORT ServiceVideoCaptureDeviceLauncher : public VideoCaptureDeviceLauncher { public: diff --git a/chromium/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc b/chromium/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc index a7e6ee8a54c..63715421590 100644 --- a/chromium/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc +++ b/chromium/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc @@ -62,6 +62,7 @@ class ServiceVideoCaptureDeviceLauncherTest : public testing::Test { &mock_source_provider_, mojo::MakeRequest(&source_provider_)); service_connection_ = base::MakeRefCounted<RefCountedVideoSourceProvider>( std::move(source_provider_), + video_capture::mojom::DeviceFactoryProviderPtr(), release_connection_cb_.Get()); launcher_ = std::make_unique<ServiceVideoCaptureDeviceLauncher>( diff --git a/chromium/content/browser/renderer_host/media/service_video_capture_provider.cc b/chromium/content/browser/renderer_host/media/service_video_capture_provider.cc index c9da8b00401..d32c8e645b6 100644 --- a/chromium/content/browser/renderer_host/media/service_video_capture_provider.cc +++ b/chromium/content/browser/renderer_host/media/service_video_capture_provider.cc @@ -14,11 +14,11 @@ #include "content/common/child_process_host_impl.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/video_capture_service.h" -#include "content/public/common/content_features.h" +#include "content/public/common/service_manager_connection.h" #include "mojo/public/cpp/bindings/callback_helpers.h" #include "mojo/public/cpp/bindings/strong_binding.h" -#include "services/video_capture/public/mojom/video_capture_service.mojom.h" +#include "services/service_manager/public/cpp/connector.h" +#include "services/video_capture/public/mojom/constants.mojom.h" #include "services/video_capture/public/uma/video_capture_service_event.h" #if defined(OS_CHROMEOS) @@ -51,82 +51,38 @@ static const int kMaxRetriesForGetDeviceInfos = 1; namespace content { -class ServiceVideoCaptureProvider::ServiceProcessObserver - : public ServiceProcessHost::Observer { - public: - ServiceProcessObserver(base::RepeatingClosure start_callback, - base::RepeatingClosure stop_callback) - : io_task_runner_( - base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO})), - start_callback_(std::move(start_callback)), - stop_callback_(std::move(stop_callback)) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - ServiceProcessHost::AddObserver(this); - } - - ~ServiceProcessObserver() override { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - ServiceProcessHost::RemoveObserver(this); - } - - private: - // ServiceProcessHost::Observer implementation. - void OnServiceProcessLaunched(const ServiceProcessInfo& info) override { - if (info.IsService<video_capture::mojom::VideoCaptureService>()) - io_task_runner_->PostTask(FROM_HERE, base::BindOnce(start_callback_)); - } - - void OnServiceProcessTerminatedNormally( - const ServiceProcessInfo& info) override { - if (info.IsService<video_capture::mojom::VideoCaptureService>()) - io_task_runner_->PostTask(FROM_HERE, base::BindOnce(stop_callback_)); - } - - void OnServiceProcessCrashed(const ServiceProcessInfo& info) override { - if (info.IsService<video_capture::mojom::VideoCaptureService>()) - io_task_runner_->PostTask(FROM_HERE, base::BindOnce(stop_callback_)); - } - - const scoped_refptr<base::TaskRunner> io_task_runner_; - const base::RepeatingClosure start_callback_; - const base::RepeatingClosure stop_callback_; - - DISALLOW_COPY_AND_ASSIGN(ServiceProcessObserver); -}; - -#if defined(OS_CHROMEOS) ServiceVideoCaptureProvider::ServiceVideoCaptureProvider( + service_manager::Connector* connector, base::RepeatingCallback<void(const std::string&)> emit_log_message_cb) - : ServiceVideoCaptureProvider(base::NullCallback(), - std::move(emit_log_message_cb)) {} + : connector_(connector ? connector->Clone() : nullptr), + emit_log_message_cb_(std::move(emit_log_message_cb)), + launcher_has_connected_to_source_provider_(false), + service_listener_binding_(this) { + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce( + &ServiceVideoCaptureProvider::RegisterServiceListenerOnIOThread, + weak_ptr_factory_.GetWeakPtr())); +} +#if defined(OS_CHROMEOS) ServiceVideoCaptureProvider::ServiceVideoCaptureProvider( CreateAcceleratorFactoryCallback create_accelerator_factory_cb, + service_manager::Connector* connector, base::RepeatingCallback<void(const std::string&)> emit_log_message_cb) - : create_accelerator_factory_cb_(std::move(create_accelerator_factory_cb)), + : connector_(connector ? connector->Clone() : nullptr), + create_accelerator_factory_cb_(std::move(create_accelerator_factory_cb)), emit_log_message_cb_(std::move(emit_log_message_cb)), - launcher_has_connected_to_source_provider_(false) { -#else // defined(OS_CHROMEOS) -ServiceVideoCaptureProvider::ServiceVideoCaptureProvider( - base::RepeatingCallback<void(const std::string&)> emit_log_message_cb) - : emit_log_message_cb_(std::move(emit_log_message_cb)), - launcher_has_connected_to_source_provider_(false) { -#endif // defined(OS_CHROMEOS) - if (features::IsVideoCaptureServiceEnabledForOutOfProcess()) { - service_process_observer_.emplace( - base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI}), - base::BindRepeating(&ServiceVideoCaptureProvider::OnServiceStarted, - weak_ptr_factory_.GetWeakPtr()), - base::BindRepeating(&ServiceVideoCaptureProvider::OnServiceStopped, - weak_ptr_factory_.GetWeakPtr())); - } else if (features::IsVideoCaptureServiceEnabledForBrowserProcess()) { - // Connect immediately and permanently when the service runs in-process. - base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}) - ->PostTask(FROM_HERE, - base::Bind(&ServiceVideoCaptureProvider::OnServiceStarted, - weak_ptr_factory_.GetWeakPtr())); - } + launcher_has_connected_to_source_provider_(false), + service_listener_binding_(this), + weak_ptr_factory_(this) { + base::PostTaskWithTraits( + FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce( + &ServiceVideoCaptureProvider::RegisterServiceListenerOnIOThread, + weak_ptr_factory_.GetWeakPtr())); } +#endif // defined(OS_CHROMEOS) ServiceVideoCaptureProvider::~ServiceVideoCaptureProvider() { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); @@ -149,8 +105,13 @@ ServiceVideoCaptureProvider::CreateDeviceLauncher() { weak_ptr_factory_.GetWeakPtr())); } -void ServiceVideoCaptureProvider::OnServiceStarted() { +void ServiceVideoCaptureProvider::OnServiceStarted( + const ::service_manager::Identity& identity, + uint32_t pid) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + if (identity.name() != video_capture::mojom::kServiceName) + return; + // Whenever the video capture service starts, we register a // VirtualVideoCaptureDevicesChangedObserver in order to propagate device // change events when virtual devices are added to or removed from the @@ -165,9 +126,13 @@ void ServiceVideoCaptureProvider::OnServiceStarted() { true /*raise_event_if_virtual_devices_already_present*/); } -void ServiceVideoCaptureProvider::OnServiceStopped() { +void ServiceVideoCaptureProvider::OnServiceStopped( + const ::service_manager::Identity& identity) { #if defined(OS_MACOSX) DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + if (identity.name() != video_capture::mojom::kServiceName) + return; + if (stashed_result_callback_for_retry_) { TRACE_EVENT_INSTANT0( TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"), @@ -181,6 +146,21 @@ void ServiceVideoCaptureProvider::OnServiceStopped() { #endif } +void ServiceVideoCaptureProvider::RegisterServiceListenerOnIOThread() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (!connector_) + return; + + service_manager::mojom::ServiceManagerListenerPtr listener; + service_listener_binding_.Bind(mojo::MakeRequest(&listener)); + + service_manager::mojom::ServiceManagerPtr service_manager; + connector_->BindInterface(service_manager::mojom::kServiceName, + &service_manager); + service_manager->AddListener(std::move(listener)); +} + void ServiceVideoCaptureProvider::OnLauncherConnectingToSourceProvider( scoped_refptr<RefCountedVideoSourceProvider>* out_provider) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); @@ -212,8 +192,14 @@ ServiceVideoCaptureProvider::LazyConnectToService() { launcher_has_connected_to_source_provider_ = false; time_of_last_connect_ = base::TimeTicks::Now(); - auto ui_task_runner = - base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI}); + DCHECK(connector_) + << "Attempted to connect to the video capture service from " + "a process that does not provide a " + "ServiceManagerConnection"; + video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider; + connector_->BindInterface(video_capture::mojom::kServiceName, + &device_factory_provider); + #if defined(OS_CHROMEOS) video_capture::mojom::AcceleratorFactoryPtr accelerator_factory; if (!create_accelerator_factory_cb_) @@ -221,18 +207,18 @@ ServiceVideoCaptureProvider::LazyConnectToService() { base::BindRepeating(&CreateAcceleratorFactory); mojo::MakeStrongBinding(create_accelerator_factory_cb_.Run(), mojo::MakeRequest(&accelerator_factory)); - GetVideoCaptureService().InjectGpuDependencies( + device_factory_provider->InjectGpuDependencies( std::move(accelerator_factory)); #endif // defined(OS_CHROMEOS) video_capture::mojom::VideoSourceProviderPtr source_provider; - GetVideoCaptureService().ConnectToVideoSourceProvider( + device_factory_provider->ConnectToVideoSourceProvider( mojo::MakeRequest(&source_provider)); source_provider.set_connection_error_handler(base::BindOnce( &ServiceVideoCaptureProvider::OnLostConnectionToSourceProvider, weak_ptr_factory_.GetWeakPtr())); auto result = base::MakeRefCounted<RefCountedVideoSourceProvider>( - std::move(source_provider), + std::move(source_provider), std::move(device_factory_provider), base::BindOnce(&ServiceVideoCaptureProvider::OnServiceConnectionClosed, weak_ptr_factory_.GetWeakPtr(), ReasonForDisconnect::kUnused)); @@ -288,13 +274,13 @@ void ServiceVideoCaptureProvider::OnDeviceInfosReceived( video_capture::uma::LogMacbookRetryGetDeviceInfosEvent( video_capture::uma::PROVIDER_RECEIVED_ZERO_INFOS_STOPPING_SERVICE); TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"), - "Waiting for video capture service to shut down.", + "Asking video capture service to shut down.", TRACE_EVENT_SCOPE_PROCESS); + service_connection->ShutdownServiceAsap(); stashed_result_callback_for_retry_ = std::move(result_callback); stashed_retry_count_ = retry_count; - - // We may try again once |OnServiceStopped()| is invoked via our - // ServiceProcessHost observer. + // Continue when service manager reports that service has shut down via + // OnServiceStopped(). return; } } diff --git a/chromium/content/browser/renderer_host/media/service_video_capture_provider.h b/chromium/content/browser/renderer_host/media/service_video_capture_provider.h index 71a3f6ed3b5..7318d843210 100644 --- a/chromium/content/browser/renderer_host/media/service_video_capture_provider.h +++ b/chromium/content/browser/renderer_host/media/service_video_capture_provider.h @@ -5,38 +5,42 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_SERVICE_VIDEO_CAPTURE_PROVIDER_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_SERVICE_VIDEO_CAPTURE_PROVIDER_H_ -#include "base/threading/sequence_bound.h" #include "base/threading/thread_checker.h" #include "build/build_config.h" #include "content/browser/renderer_host/media/ref_counted_video_source_provider.h" #include "content/browser/renderer_host/media/video_capture_provider.h" -#include "content/public/browser/service_process_host.h" -#include "services/video_capture/public/mojom/video_capture_service.mojom.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "services/service_manager/public/cpp/connector.h" +#include "services/service_manager/public/mojom/service_manager.mojom.h" +#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" namespace content { -// Implementation of VideoCaptureProvider that uses -// video_capture::mojom::VideoCaptureService. -// +// Implementation of VideoCaptureProvider that uses the "video_capture" service. // Connects to the service lazily on demand and disconnects from the service as // soon as all previously handed out VideoCaptureDeviceLauncher instances have // been released and no more answers to GetDeviceInfosAsync() calls are pending. +// It is legal to create instances using |nullptr| as |connector| but for +// instances produced this way, it is illegal to subsequently call any of the +// public methods. class CONTENT_EXPORT ServiceVideoCaptureProvider : public VideoCaptureProvider, - public ServiceProcessHost::Observer { + public service_manager::mojom::ServiceManagerListener { public: // This constructor uses a default factory for instances of // viz::mojom::Gpu which produces instances of class content::GpuClient. - explicit ServiceVideoCaptureProvider( + ServiceVideoCaptureProvider( + service_manager::Connector* connector, base::RepeatingCallback<void(const std::string&)> emit_log_message_cb); #if defined(OS_CHROMEOS) using CreateAcceleratorFactoryCallback = base::RepeatingCallback< std::unique_ptr<video_capture::mojom::AcceleratorFactory>()>; - // Lets clients provide a custom factory method for creating instances of - // viz::mojom::Gpu. + // Lets clients provide a custom mojo::Connector and factory method for + // creating instances of viz::mojom::Gpu. ServiceVideoCaptureProvider( CreateAcceleratorFactoryCallback create_accelerator_factory_cb, + service_manager::Connector* connector, base::RepeatingCallback<void(const std::string&)> emit_log_message_cb); #endif // defined(OS_CHROMEOS) @@ -46,12 +50,25 @@ class CONTENT_EXPORT ServiceVideoCaptureProvider void GetDeviceInfosAsync(GetDeviceInfosCallback result_callback) override; std::unique_ptr<VideoCaptureDeviceLauncher> CreateDeviceLauncher() override; - private: - void OnServiceStarted(); - void OnServiceStopped(); + // service_manager::mojom::ServiceManagerListener implementation. + void OnInit(std::vector<service_manager::mojom::RunningServiceInfoPtr> + running_services) override {} + void OnServiceCreated( + service_manager::mojom::RunningServiceInfoPtr service) override {} + void OnServiceStarted(const ::service_manager::Identity& identity, + uint32_t pid) override; + void OnServicePIDReceived(const ::service_manager::Identity& identity, + uint32_t pid) override {} + void OnServiceFailedToStart( + const ::service_manager::Identity& identity) override {} + void OnServiceStopped(const ::service_manager::Identity& identity) override; + private: enum class ReasonForDisconnect { kShutdown, kUnused, kConnectionLost }; + void RegisterServiceListenerOnIOThread(); + // Note, this needs to have void return value because of "weak_ptrs can only + // bind to methods without return values". void OnLauncherConnectingToSourceProvider( scoped_refptr<RefCountedVideoSourceProvider>* out_provider); // Discarding the returned RefCountedVideoSourceProvider indicates that the @@ -74,6 +91,7 @@ class CONTENT_EXPORT ServiceVideoCaptureProvider void OnLostConnectionToSourceProvider(); void OnServiceConnectionClosed(ReasonForDisconnect reason); + std::unique_ptr<service_manager::Connector> connector_; #if defined(OS_CHROMEOS) CreateAcceleratorFactoryCallback create_accelerator_factory_cb_; #endif // defined(OS_CHROMEOS) @@ -85,16 +103,14 @@ class CONTENT_EXPORT ServiceVideoCaptureProvider base::TimeTicks time_of_last_connect_; base::TimeTicks time_of_last_uninitialize_; + mojo::Binding<service_manager::mojom::ServiceManagerListener> + service_listener_binding_; + #if defined(OS_MACOSX) GetDeviceInfosCallback stashed_result_callback_for_retry_; int stashed_retry_count_; #endif - // We own this but it must operate on the UI thread. - class ServiceProcessObserver; - base::Optional<base::SequenceBound<ServiceProcessObserver>> - service_process_observer_; - base::WeakPtrFactory<ServiceVideoCaptureProvider> weak_ptr_factory_{this}; }; diff --git a/chromium/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc b/chromium/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc index 41df3156200..ca82f24746d 100644 --- a/chromium/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc +++ b/chromium/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc @@ -9,20 +9,22 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/run_loop.h" -#include "base/test/bind_test_util.h" #include "base/test/mock_callback.h" #include "base/threading/thread.h" #include "content/public/browser/video_capture_device_launcher.h" -#include "content/public/browser/video_capture_service.h" #include "content/public/test/test_browser_thread_bundle.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/binding_set.h" +#include "services/service_manager/public/cpp/binder_registry.h" +#include "services/service_manager/public/cpp/service.h" +#include "services/service_manager/public/cpp/service_binding.h" +#include "services/service_manager/public/cpp/test/test_connector_factory.h" +#include "services/video_capture/public/cpp/mock_device_factory_provider.h" #include "services/video_capture/public/cpp/mock_push_subscription.h" -#include "services/video_capture/public/cpp/mock_video_capture_service.h" #include "services/video_capture/public/cpp/mock_video_source.h" #include "services/video_capture/public/cpp/mock_video_source_provider.h" +#include "services/video_capture/public/mojom/constants.mojom.h" #include "services/video_capture/public/mojom/producer.mojom.h" -#include "services/video_capture/public/mojom/video_capture_service.mojom.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -38,6 +40,49 @@ static const base::WeakPtr<media::VideoFrameReceiver> kNullReceiver; static const auto kIgnoreLogMessageCB = base::BindRepeating([](const std::string&) {}); +class FakeVideoCaptureService : public service_manager::Service { + public: + explicit FakeVideoCaptureService( + service_manager::mojom::ServiceRequest request) + : binding_(this, std::move(request)) {} + + void OnStart() { + registry_.AddInterface<video_capture::mojom::DeviceFactoryProvider>( + // Unretained |this| is safe because |registry_| is owned by |this|. + base::BindRepeating( + &FakeVideoCaptureService::OnDeviceFactoryProviderRequest, + base::Unretained(this))); + } + + void OnBindInterface(const service_manager::BindSourceInfo& source_info, + const std::string& interface_name, + mojo::ScopedMessagePipeHandle interface_pipe) override { + registry_.BindInterface(interface_name, std::move(interface_pipe)); + } + + void OnDeviceFactoryProviderRequest( + video_capture::mojom::DeviceFactoryProviderRequest request) { + BindFactoryProvider(&request); + } + + MOCK_METHOD1( + BindFactoryProvider, + void(video_capture::mojom::DeviceFactoryProviderRequest* request)); + + private: + service_manager::ServiceBinding binding_; + service_manager::BinderRegistry registry_; +}; + +class NullService : public service_manager::Service { + public: + explicit NullService(service_manager::mojom::ServiceRequest request) + : binding_(this, std::move(request)) {} + + private: + service_manager::ServiceBinding binding_; +}; + class MockVideoCaptureDeviceLauncherCallbacks : public VideoCaptureDeviceLauncher::Callbacks { public: @@ -55,13 +100,14 @@ class MockVideoCaptureDeviceLauncherCallbacks class ServiceVideoCaptureProviderTest : public testing::Test { public: ServiceVideoCaptureProviderTest() - : source_provider_binding_(&mock_source_provider_) { - OverrideVideoCaptureServiceForTesting(&mock_video_capture_service_); - } - - ~ServiceVideoCaptureProviderTest() override { - OverrideVideoCaptureServiceForTesting(nullptr); - } + : fake_video_capture_service_(connector_factory_.RegisterInstance( + video_capture::mojom::kServiceName)), + fake_service_manager_(connector_factory_.RegisterInstance( + service_manager::mojom::kServiceName)), + factory_provider_binding_(&mock_device_factory_provider_), + factory_provider_is_bound_(false), + source_provider_binding_(&mock_source_provider_) {} + ~ServiceVideoCaptureProviderTest() override {} protected: void SetUp() override { @@ -70,13 +116,28 @@ class ServiceVideoCaptureProviderTest : public testing::Test { base::BindRepeating([]() { return std::unique_ptr<video_capture::mojom::AcceleratorFactory>(); }), - kIgnoreLogMessageCB); + connector_factory_.GetDefaultConnector(), kIgnoreLogMessageCB); #else - provider_ = - std::make_unique<ServiceVideoCaptureProvider>(kIgnoreLogMessageCB); + provider_ = std::make_unique<ServiceVideoCaptureProvider>( + connector_factory_.GetDefaultConnector(), kIgnoreLogMessageCB); #endif // defined(OS_CHROMEOS) - ON_CALL(mock_video_capture_service_, DoConnectToVideoSourceProvider(_)) + ON_CALL(fake_video_capture_service_, BindFactoryProvider(_)) + .WillByDefault(Invoke( + [this]( + video_capture::mojom::DeviceFactoryProviderRequest* request) { + if (factory_provider_binding_.is_bound()) + factory_provider_binding_.Close(); + factory_provider_binding_.Bind(std::move(*request)); + factory_provider_is_bound_ = true; + factory_provider_binding_.set_connection_error_handler( + base::BindOnce( + [](bool* factory_provider_is_bound) { + *factory_provider_is_bound = false; + }, + &factory_provider_is_bound_)); + })); + ON_CALL(mock_device_factory_provider_, DoConnectToVideoSourceProvider(_)) .WillByDefault(Invoke( [this](video_capture::mojom::VideoSourceProviderRequest& request) { if (source_provider_binding_.is_bound()) @@ -120,7 +181,13 @@ class ServiceVideoCaptureProviderTest : public testing::Test { void TearDown() override {} content::TestBrowserThreadBundle test_browser_thread_bundle_; - video_capture::MockVideoCaptureService mock_video_capture_service_; + service_manager::TestConnectorFactory connector_factory_; + FakeVideoCaptureService fake_video_capture_service_; + NullService fake_service_manager_; + video_capture::MockDeviceFactoryProvider mock_device_factory_provider_; + mojo::Binding<video_capture::mojom::DeviceFactoryProvider> + factory_provider_binding_; + bool factory_provider_is_bound_; video_capture::MockVideoSourceProvider mock_source_provider_; mojo::Binding<video_capture::mojom::VideoSourceProvider> source_provider_binding_; @@ -175,6 +242,8 @@ TEST_F(ServiceVideoCaptureProviderTest, // Simulate that the service goes down by cutting the connections. source_provider_binding_.Close(); + factory_provider_binding_.Close(); + wait_for_callback_from_service.Run(); } @@ -204,9 +273,13 @@ TEST_F(ServiceVideoCaptureProviderTest, // Setup part 2: Now that the connection to the service is established, we can // listen for disconnects. base::RunLoop wait_for_connection_to_source_provider_to_close; + base::RunLoop wait_for_connection_to_device_factory_provider_to_close; source_provider_binding_.set_connection_error_handler( base::BindOnce([](base::RunLoop* run_loop) { run_loop->Quit(); }, &wait_for_connection_to_source_provider_to_close)); + factory_provider_binding_.set_connection_error_handler( + base::BindOnce([](base::RunLoop* run_loop) { run_loop->Quit(); }, + &wait_for_connection_to_device_factory_provider_to_close)); // Exercise part 2: The service responds std::vector<media::VideoCaptureDeviceInfo> arbitrarily_empty_results; @@ -214,6 +287,9 @@ TEST_F(ServiceVideoCaptureProviderTest, // Verification: Expect |provider_| to close the connection to the service. wait_for_connection_to_source_provider_to_close.Run(); + if (factory_provider_is_bound_) { + wait_for_connection_to_device_factory_provider_to_close.Run(); + } } // Tests that |ServiceVideoCaptureProvider| does not close the connection to the @@ -241,6 +317,11 @@ TEST_F(ServiceVideoCaptureProviderTest, *connection_has_been_closed = true; }, &connection_has_been_closed)); + factory_provider_binding_.set_connection_error_handler(base::BindOnce( + [](bool* connection_has_been_closed) { + *connection_has_been_closed = true; + }, + &connection_has_been_closed)); // Exercise part 2: Make a few GetDeviceInfosAsync requests base::RunLoop wait_for_get_device_infos_response_1; @@ -332,6 +413,11 @@ TEST_F(ServiceVideoCaptureProviderTest, *connection_has_been_closed = true; }, &connection_has_been_closed)); + factory_provider_binding_.set_connection_error_handler(base::BindOnce( + [](bool* connection_has_been_closed) { + *connection_has_been_closed = true; + }, + &connection_has_been_closed)); // The service now responds to the first request. std::vector<media::VideoCaptureDeviceInfo> arbitrarily_empty_results; diff --git a/chromium/content/browser/renderer_host/media/video_capture_manager.cc b/chromium/content/browser/renderer_host/media/video_capture_manager.cc index c5fa1a0e74e..14b318cbd78 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_manager.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_manager.cc @@ -236,16 +236,14 @@ void VideoCaptureManager::DoStopDevice(VideoCaptureController* controller) { // If start request has not yet started processing, i.e. if it is not at the // beginning of the queue, remove it from the queue. - auto request_iter = device_start_request_queue_.begin(); - if (request_iter != device_start_request_queue_.end()) { - request_iter = - std::find_if(++request_iter, device_start_request_queue_.end(), - [controller](const CaptureDeviceStartRequest& request) { - return request.controller() == controller; - }); - if (request_iter != device_start_request_queue_.end()) { - device_start_request_queue_.erase(request_iter); - return; + if (!device_start_request_queue_.empty()) { + auto second_request = std::next(device_start_request_queue_.begin()); + + for (auto it = second_request; it != device_start_request_queue_.end();) { + if (it->controller() == controller) + it = device_start_request_queue_.erase(it); + else + ++it; } } @@ -641,8 +639,7 @@ void VideoCaptureManager::GetPhotoState( media::VideoCaptureDevice::GetPhotoStateCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - const VideoCaptureController* controller = - LookupControllerBySessionId(session_id); + VideoCaptureController* controller = LookupControllerBySessionId(session_id); if (!controller) return; if (controller->IsDeviceAlive()) { @@ -653,7 +650,7 @@ void VideoCaptureManager::GetPhotoState( photo_request_queue_.emplace_back( session_id, base::Bind(&VideoCaptureController::GetPhotoState, - base::Unretained(controller), base::Passed(&callback))); + controller->GetWeakPtrForIOThread(), base::Passed(&callback))); } void VideoCaptureManager::SetPhotoOptions( @@ -672,7 +669,7 @@ void VideoCaptureManager::SetPhotoOptions( // Queue up a request for later. photo_request_queue_.emplace_back( session_id, base::Bind(&VideoCaptureController::SetPhotoOptions, - base::Unretained(controller), + controller->GetWeakPtrForIOThread(), base::Passed(&settings), base::Passed(&callback))); } @@ -698,7 +695,7 @@ void VideoCaptureManager::TakePhoto( photo_request_queue_.emplace_back( session_id, base::Bind(&VideoCaptureController::TakePhoto, - base::Unretained(controller), base::Passed(&callback))); + controller->GetWeakPtrForIOThread(), base::Passed(&callback))); } void VideoCaptureManager::OnOpened( diff --git a/chromium/content/browser/service_manager/service_manager_context.cc b/chromium/content/browser/service_manager/service_manager_context.cc index 2ac4547aabd..c747a954ef7 100644 --- a/chromium/content/browser/service_manager/service_manager_context.cc +++ b/chromium/content/browser/service_manager/service_manager_context.cc @@ -87,6 +87,8 @@ #include "services/tracing/public/cpp/tracing_features.h" #include "services/tracing/public/mojom/constants.mojom.h" #include "services/tracing/tracing_service.h" +#include "services/video_capture/public/mojom/constants.mojom.h" +#include "services/video_capture/service_impl.h" #include "ui/base/buildflags.h" #include "ui/base/ui_base_features.h" @@ -317,6 +319,13 @@ void RegisterInProcessService( &LaunchInProcessService, std::move(task_runner), factory); } +std::unique_ptr<service_manager::Service> CreateVideoCaptureService( + service_manager::mojom::ServiceRequest request) { + return std::make_unique<video_capture::ServiceImpl>( + std::move(request), base::CreateSingleThreadTaskRunnerWithTraits( + {content::BrowserThread::UI})); +} + void CreateInProcessAudioService( scoped_refptr<base::SequencedTaskRunner> task_runner, service_manager::mojom::ServiceRequest request) { @@ -634,6 +643,20 @@ ServiceManagerContext::ServiceManagerContext( base::BindRepeating(&CreateMediaSessionService)); } + if (features::IsVideoCaptureServiceEnabledForBrowserProcess()) { + RegisterInProcessService( + video_capture::mojom::kServiceName, +#if defined(OS_WIN) + base::CreateCOMSTATaskRunnerWithTraits( +#else + base::CreateSingleThreadTaskRunnerWithTraits( +#endif + base::TaskTraits({base::MayBlock(), base::WithBaseSyncPrimitives(), + base::TaskPriority::BEST_EFFORT}), + base::SingleThreadTaskRunnerThreadMode::DEDICATED), + base::BindRepeating(&CreateVideoCaptureService)); + } + #if defined(OS_LINUX) RegisterInProcessService( font_service::mojom::kServiceName, diff --git a/chromium/content/browser/service_process_host_impl.cc b/chromium/content/browser/service_process_host_impl.cc index 50034eadc8f..f41cf58a87c 100644 --- a/chromium/content/browser/service_process_host_impl.cc +++ b/chromium/content/browser/service_process_host_impl.cc @@ -173,7 +173,6 @@ void LaunchServiceProcessOnIOThread(mojo::GenericPendingReceiver receiver, : base::UTF8ToUTF16(*receiver.interface_name())); host->SetMetricsName(*receiver.interface_name()); host->SetSandboxType(options.sandbox_type); - host->SetExtraCommandLineSwitches(std::move(options.extra_switches)); if (options.child_flags) host->set_child_flags(*options.child_flags); host->Start(); diff --git a/chromium/content/browser/service_worker/service_worker_version.cc b/chromium/content/browser/service_worker/service_worker_version.cc index c11b6b26102..f1760434998 100644 --- a/chromium/content/browser/service_worker/service_worker_version.cc +++ b/chromium/content/browser/service_worker/service_worker_version.cc @@ -1183,6 +1183,15 @@ void ServiceWorkerVersion::OpenPaymentHandlerWindow( return; } + if (!url.is_valid() || + !url::Origin::Create(url).IsSameOriginWith(script_origin_)) { + mojo::ReportBadMessage( + "Received PaymentRequestEvent#openWindow() request for a cross-origin " + "URL."); + binding_.Close(); + return; + } + PaymentHandlerSupport::ShowPaymentHandlerWindow( url, context_.get(), base::BindOnce(&DidShowPaymentHandlerWindow, url, context_), diff --git a/chromium/content/browser/utility_process_host.cc b/chromium/content/browser/utility_process_host.cc index 85b12bc86b7..73aca4bb8ec 100644 --- a/chromium/content/browser/utility_process_host.cc +++ b/chromium/content/browser/utility_process_host.cc @@ -298,11 +298,6 @@ void UtilityProcessHost::SetServiceIdentity( service_identity_ = identity; } -void UtilityProcessHost::SetExtraCommandLineSwitches( - std::vector<std::string> switches) { - extra_switches_ = std::move(switches); -} - mojom::ChildProcess* UtilityProcessHost::GetChildProcess() { return static_cast<ChildProcessHostImpl*>(process_->GetHost()) ->child_process(); @@ -455,9 +450,6 @@ bool UtilityProcessHost::StartProcess() { *service_identity_, cmd_line.get()); } - for (const auto& extra_switch : extra_switches_) - cmd_line->AppendSwitch(extra_switch); - std::unique_ptr<UtilitySandboxedProcessLauncherDelegate> delegate = std::make_unique<UtilitySandboxedProcessLauncherDelegate>( sandbox_type_, env_, *cmd_line); diff --git a/chromium/content/browser/utility_process_host.h b/chromium/content/browser/utility_process_host.h index 8233364fdf6..e3417c485a9 100644 --- a/chromium/content/browser/utility_process_host.h +++ b/chromium/content/browser/utility_process_host.h @@ -117,9 +117,6 @@ class CONTENT_EXPORT UtilityProcessHost // the identity of the service being launched. void SetServiceIdentity(const service_manager::Identity& identity); - // Provides extra switches to append to the process's command line. - void SetExtraCommandLineSwitches(std::vector<std::string> switches); - // Returns a control interface for the running child process. mojom::ChildProcess* GetChildProcess(); @@ -164,9 +161,6 @@ class CONTENT_EXPORT UtilityProcessHost // service. base::Optional<service_manager::Identity> service_identity_; - // Extra command line switches to append. - std::vector<std::string> extra_switches_; - // Indicates whether the process has been successfully launched yet, or if // launch failed. enum class LaunchState { diff --git a/chromium/content/browser/video_capture_service.cc b/chromium/content/browser/video_capture_service.cc deleted file mode 100644 index 4f9cdfb270b..00000000000 --- a/chromium/content/browser/video_capture_service.cc +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2019 The Chromium Authors. 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/video_capture_service.h" - -#include "base/no_destructor.h" -#include "base/task/post_task.h" -#include "base/threading/sequence_local_storage_slot.h" -#include "base/time/time.h" -#include "build/build_config.h" -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/service_process_host.h" -#include "content/public/common/content_features.h" -#include "content/public/common/content_switches.h" -#include "mojo/public/cpp/bindings/receiver_set.h" -#include "mojo/public/cpp/bindings/remote.h" -#include "mojo/public/cpp/bindings/self_owned_receiver.h" -#include "services/video_capture/public/uma/video_capture_service_event.h" -#include "services/video_capture/video_capture_service_impl.h" - -#if defined(OS_WIN) -#define CREATE_IN_PROCESS_TASK_RUNNER base::CreateCOMSTATaskRunnerWithTraits -#else -#define CREATE_IN_PROCESS_TASK_RUNNER \ - base::CreateSingleThreadTaskRunnerWithTraits -#endif - -namespace content { - -namespace { - -video_capture::mojom::VideoCaptureService* g_service_override = nullptr; - -void BindInProcessInstance( - mojo::PendingReceiver<video_capture::mojom::VideoCaptureService> receiver) { - static base::NoDestructor<video_capture::VideoCaptureServiceImpl> service( - std::move(receiver), - base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI})); -} - -mojo::Remote<video_capture::mojom::VideoCaptureService>& GetUIThreadRemote() { - static base::NoDestructor< - mojo::Remote<video_capture::mojom::VideoCaptureService>> - remote; - return *remote; -} - -// This is a custom traits type we use in conjunction with mojo::ReceiverSetBase -// so that all dispatched messages can be forwarded to the currently bound UI -// thread Remote. -struct ForwardingImplRefTraits { - using PointerType = void*; - static bool IsNull(PointerType) { return false; } - static video_capture::mojom::VideoCaptureService* GetRawPointer(PointerType) { - return &GetVideoCaptureService(); - } -}; - -// If |GetVideoCaptureService()| is called from off the UI thread, return a -// sequence-local Remote. Its corresponding receiver will be bound in this set, -// forwarding to the current UI-thread Remote. -void BindProxyRemoteOnUIThread( - mojo::PendingReceiver<video_capture::mojom::VideoCaptureService> receiver) { - static base::NoDestructor<mojo::ReceiverSetBase< - mojo::Receiver<video_capture::mojom::VideoCaptureService, - ForwardingImplRefTraits>, - void>> - receivers; - receivers->Add(nullptr, std::move(receiver)); -} - -} // namespace - -video_capture::mojom::VideoCaptureService& GetVideoCaptureService() { - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - static base::NoDestructor<base::SequenceLocalStorageSlot< - mojo::Remote<video_capture::mojom::VideoCaptureService>>> - storage; - auto& remote = storage->GetOrCreateValue(); - if (!remote.is_bound()) { - base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI}) - ->PostTask(FROM_HERE, - base::BindOnce(&BindProxyRemoteOnUIThread, - remote.BindNewPipeAndPassReceiver())); - } - return *remote.get(); - } - - if (g_service_override) - return *g_service_override; - - auto& remote = GetUIThreadRemote(); - if (!remote.is_bound()) { - auto receiver = remote.BindNewPipeAndPassReceiver(); - if (features::IsVideoCaptureServiceEnabledForBrowserProcess()) { - auto dedicated_task_runner = CREATE_IN_PROCESS_TASK_RUNNER( - base::TaskTraits({base::MayBlock(), base::WithBaseSyncPrimitives(), - base::TaskPriority::BEST_EFFORT}), - base::SingleThreadTaskRunnerThreadMode::DEDICATED); - dedicated_task_runner->PostTask( - FROM_HERE, - base::BindOnce(&BindInProcessInstance, std::move(receiver))); - } else { - ServiceProcessHost::Launch( - std::move(receiver), - ServiceProcessHost::Options() - .WithDisplayName("Video Capture") - .WithSandboxType(service_manager::SANDBOX_TYPE_NO_SANDBOX) -#if defined(OS_MACOSX) - // On Mac, the service requires a CFRunLoop which is provided by a - // UI message loop. See https://crbug.com/834581. - .WithExtraCommandLineSwitches({switches::kMessageLoopTypeUi}) -#endif - .Pass()); - -#if !defined(OS_ANDROID) - // On Android, we do not use automatic service shutdown, because when - // shutting down the service, we lose caching of the supported formats, - // and re-querying these can take several seconds on certain Android - // devices. - remote.set_idle_handler( - base::TimeDelta::FromSeconds(5), - base::BindRepeating( - [](mojo::Remote<video_capture::mojom::VideoCaptureService>* - remote) { - video_capture::uma::LogVideoCaptureServiceEvent( - video_capture::uma :: - SERVICE_SHUTTING_DOWN_BECAUSE_NO_CLIENT); - remote->reset(); - }, - &remote)); -#endif // !defined(OS_ANDROID) - - // Make sure the Remote is also reset in case of e.g. service crash so we - // can restart it as needed. - remote.reset_on_disconnect(); - } - } - - return *remote.get(); -} - -void OverrideVideoCaptureServiceForTesting( - video_capture::mojom::VideoCaptureService* service) { - g_service_override = service; -} - -} // namespace content diff --git a/chromium/content/browser/webrtc/webrtc_video_capture_browsertest.cc b/chromium/content/browser/webrtc/webrtc_video_capture_browsertest.cc index e2ad80cef10..03ad9fb36c8 100644 --- a/chromium/content/browser/webrtc/webrtc_video_capture_browsertest.cc +++ b/chromium/content/browser/webrtc/webrtc_video_capture_browsertest.cc @@ -7,7 +7,7 @@ #include "build/build_config.h" #include "content/browser/webrtc/webrtc_webcam_browsertest.h" #include "content/public/browser/browser_child_process_host.h" -#include "content/public/browser/video_capture_service.h" +#include "content/public/browser/system_connector.h" #include "content/public/common/child_process_host.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" @@ -16,6 +16,8 @@ #include "content/shell/browser/shell.h" #include "media/base/media_switches.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "services/service_manager/public/cpp/connector.h" +#include "services/video_capture/public/mojom/constants.mojom.h" #include "services/video_capture/public/mojom/testing_controls.mojom.h" namespace content { @@ -79,8 +81,8 @@ IN_PROC_BROWSER_TEST_F(WebRtcVideoCaptureBrowserTest, // Simulate crash in video capture process video_capture::mojom::TestingControlsPtr service_controls; - GetVideoCaptureService().BindControlsForTesting( - mojo::MakeRequest(&service_controls)); + GetSystemConnector()->BindInterface(video_capture::mojom::kServiceName, + mojo::MakeRequest(&service_controls)); service_controls->Crash(); // Wait for video element to turn black diff --git a/chromium/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc b/chromium/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc index 9c76c55d9d9..2dee5d94b66 100644 --- a/chromium/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc +++ b/chromium/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc @@ -10,7 +10,7 @@ #include "cc/base/math_util.h" #include "components/viz/common/gpu/context_provider.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/video_capture_service.h" +#include "content/public/browser/system_connector.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" @@ -26,8 +26,10 @@ #include "media/capture/video/shared_memory_handle_provider.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "services/service_manager/public/cpp/connector.h" #include "services/video_capture/public/mojom/constants.mojom.h" #include "services/video_capture/public/mojom/device_factory.mojom.h" +#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" #include "services/video_capture/public/mojom/producer.mojom.h" #include "services/video_capture/public/mojom/scoped_access_permission.mojom.h" #include "services/video_capture/public/mojom/virtual_device.mojom.h" @@ -393,11 +395,11 @@ class SharedMemoryDeviceExerciser : public VirtualDeviceExerciser, base::WeakPtrFactory<SharedMemoryDeviceExerciser> weak_factory_{this}; }; -// Integration test that obtains a connection to the video capture service. It -// It then registers a virtual device at the service and feeds frames to it. It -// opens the virtual device in a <video> element on a test page and verifies -// that the element plays in the expected dimensions and the pixel content on -// the element changes. +// Integration test that obtains a connection to the video capture service via +// the Browser process' service manager. It then registers a virtual device at +// the service and feeds frames to it. It opens the virtual device in a <video> +// element on a test page and verifies that the element plays in the expected +// dimenstions and the pixel content on the element changes. class WebRtcVideoCaptureServiceBrowserTest : public ContentBrowserTest { public: WebRtcVideoCaptureServiceBrowserTest() @@ -411,14 +413,8 @@ class WebRtcVideoCaptureServiceBrowserTest : public ContentBrowserTest { void AddVirtualDeviceAndStartCapture(VirtualDeviceExerciser* device_exerciser, base::OnceClosure finish_test_cb) { DCHECK(virtual_device_thread_.task_runner()->RunsTasksInCurrentSequence()); - - main_task_runner_->PostTask( - FROM_HERE, base::BindOnce( - [](video_capture::mojom::DeviceFactoryRequest request) { - GetVideoCaptureService().ConnectToDeviceFactory( - std::move(request)); - }, - mojo::MakeRequest(&factory_))); + connector_->BindInterface(video_capture::mojom::kServiceName, &provider_); + provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory_)); media::VideoCaptureDeviceInfo info; info.descriptor.device_id = kVirtualDeviceId; @@ -461,6 +457,7 @@ class WebRtcVideoCaptureServiceBrowserTest : public ContentBrowserTest { LOG(INFO) << "Shutting down virtual device"; device_exerciser->ShutDown(); factory_ = nullptr; + provider_ = nullptr; weak_factory_.InvalidateWeakPtrs(); std::move(continuation).Run(); } @@ -500,10 +497,16 @@ class WebRtcVideoCaptureServiceBrowserTest : public ContentBrowserTest { void Initialize() { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); main_task_runner_ = base::ThreadTaskRunnerHandle::Get(); + + auto* connector = GetSystemConnector(); + ASSERT_TRUE(connector); + // We need to clone it so that we can use the clone on a different thread. + connector_ = connector->Clone(); } base::Thread virtual_device_thread_; scoped_refptr<base::TaskRunner> main_task_runner_; + std::unique_ptr<service_manager::Connector> connector_; private: base::TimeDelta CalculateTimeSinceFirstInvocation() { @@ -513,6 +516,7 @@ class WebRtcVideoCaptureServiceBrowserTest : public ContentBrowserTest { } base::test::ScopedFeatureList scoped_feature_list_; + video_capture::mojom::DeviceFactoryProviderPtr provider_; video_capture::mojom::DeviceFactoryPtr factory_; gfx::Size video_size_; base::TimeTicks first_frame_time_; diff --git a/chromium/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc b/chromium/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc index aa96d377646..7a991574051 100644 --- a/chromium/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc +++ b/chromium/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc @@ -6,7 +6,7 @@ #include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/video_capture_service.h" +#include "content/public/browser/system_connector.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" @@ -16,8 +16,11 @@ #include "media/base/media_switches.h" #include "mojo/public/cpp/bindings/binding.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "services/service_manager/public/cpp/connector.h" #include "services/video_capture/public/cpp/mock_producer.h" +#include "services/video_capture/public/mojom/constants.mojom.h" #include "services/video_capture/public/mojom/device_factory.mojom.h" +#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" #include "services/video_capture/public/mojom/devices_changed_observer.mojom.h" #include "services/video_capture/public/mojom/producer.mojom.h" #include "services/video_capture/public/mojom/video_source_provider.mojom.h" @@ -72,18 +75,18 @@ class WebRtcVideoCaptureServiceEnumerationBrowserTest ~WebRtcVideoCaptureServiceEnumerationBrowserTest() override {} void ConnectToService() { + connector_->BindInterface(video_capture::mojom::kServiceName, &provider_); video_capture::mojom::DevicesChangedObserverPtr observer; devices_changed_observer_binding_.Bind(mojo::MakeRequest(&observer)); switch (GetParam().api_to_use) { case ServiceApi::kSingleClient: - GetVideoCaptureService().ConnectToDeviceFactory( - mojo::MakeRequest(&factory_)); + provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory_)); factory_->RegisterVirtualDevicesChangedObserver( std::move(observer), false /*raise_event_if_virtual_devices_already_present*/); break; case ServiceApi::kMultiClient: - GetVideoCaptureService().ConnectToVideoSourceProvider( + provider_->ConnectToVideoSourceProvider( mojo::MakeRequest(&video_source_provider_)); video_source_provider_->RegisterVirtualDevicesChangedObserver( std::move(observer), @@ -163,6 +166,7 @@ class WebRtcVideoCaptureServiceEnumerationBrowserTest void DisconnectFromService() { factory_ = nullptr; video_source_provider_ = nullptr; + provider_ = nullptr; } void EnumerateDevicesInRendererAndVerifyDeviceCount( @@ -214,8 +218,13 @@ class WebRtcVideoCaptureServiceEnumerationBrowserTest NavigateToURL(shell(), GURL(embedded_test_server()->GetURL(kVideoCaptureHtmlFile))); + + auto* connector = GetSystemConnector(); + ASSERT_TRUE(connector); + connector_ = connector->Clone(); } + std::unique_ptr<service_manager::Connector> connector_; std::map<std::string, video_capture::mojom::TextureVirtualDevicePtr> texture_devices_by_id_; std::map<std::string, @@ -227,6 +236,7 @@ class WebRtcVideoCaptureServiceEnumerationBrowserTest mojo::Binding<video_capture::mojom::DevicesChangedObserver> devices_changed_observer_binding_; base::test::ScopedFeatureList scoped_feature_list_; + video_capture::mojom::DeviceFactoryProviderPtr provider_; video_capture::mojom::DeviceFactoryPtr factory_; video_capture::mojom::VideoSourceProviderPtr video_source_provider_; base::OnceClosure closure_to_be_called_on_devices_changed_; diff --git a/chromium/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc b/chromium/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc index 5b5ae1c4163..af48c410838 100644 --- a/chromium/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc +++ b/chromium/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc @@ -6,16 +6,19 @@ #include "base/run_loop.h" #include "base/test/scoped_feature_list.h" #include "build/build_config.h" -#include "content/public/browser/video_capture_service.h" +#include "content/public/browser/system_connector.h" #include "content/public/common/content_features.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 "media/base/media_switches.h" +#include "services/service_manager/public/cpp/connector.h" #include "services/video_capture/public/cpp/mock_receiver.h" +#include "services/video_capture/public/mojom/constants.mojom.h" #include "services/video_capture/public/mojom/device.mojom.h" #include "services/video_capture/public/mojom/device_factory.mojom.h" +#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" #include "services/video_capture/public/mojom/video_source.mojom.h" #include "services/video_capture/public/mojom/video_source_provider.mojom.h" #include "testing/gmock/include/gmock/gmock.h" @@ -77,16 +80,18 @@ class WebRtcVideoCaptureSharedDeviceBrowserTest ~WebRtcVideoCaptureSharedDeviceBrowserTest() override {} void OpenDeviceViaService() { + connector_->BindInterface(video_capture::mojom::kServiceName, + &device_factory_provider_); switch (GetParam().api_to_use) { case ServiceApi::kSingleClient: - GetVideoCaptureService().ConnectToDeviceFactory( + device_factory_provider_->ConnectToDeviceFactory( mojo::MakeRequest(&device_factory_)); device_factory_->GetDeviceInfos(base::BindOnce( &WebRtcVideoCaptureSharedDeviceBrowserTest::OnDeviceInfosReceived, weak_factory_.GetWeakPtr(), GetParam().buffer_type_to_request)); break; case ServiceApi::kMultiClient: - GetVideoCaptureService().ConnectToVideoSourceProvider( + device_factory_provider_->ConnectToVideoSourceProvider( mojo::MakeRequest(&video_source_provider_)); video_source_provider_->GetSourceInfos(base::BindOnce( &WebRtcVideoCaptureSharedDeviceBrowserTest::OnSourceInfosReceived, @@ -125,11 +130,18 @@ class WebRtcVideoCaptureSharedDeviceBrowserTest void Initialize() { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); main_task_runner_ = base::ThreadTaskRunnerHandle::Get(); + + auto* connector = GetSystemConnector(); + ASSERT_TRUE(connector); + // We need to clone it so that we can use the clone on a different thread. + connector_ = connector->Clone(); + mock_receiver_ = std::make_unique<video_capture::MockReceiver>( mojo::MakeRequest(&receiver_proxy_)); } scoped_refptr<base::TaskRunner> main_task_runner_; + std::unique_ptr<service_manager::Connector> connector_; std::unique_ptr<video_capture::MockReceiver> mock_receiver_; private: @@ -192,6 +204,7 @@ class WebRtcVideoCaptureSharedDeviceBrowserTest } base::test::ScopedFeatureList scoped_feature_list_; + video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider_; // For single-client API case only video_capture::mojom::DeviceFactoryPtr device_factory_; diff --git a/chromium/content/public/browser/BUILD.gn b/chromium/content/public/browser/BUILD.gn index c6bf6053793..a595c4aa6b3 100644 --- a/chromium/content/public/browser/BUILD.gn +++ b/chromium/content/public/browser/BUILD.gn @@ -334,7 +334,6 @@ jumbo_source_set("browser_sources") { "url_loader_request_interceptor.h", "video_capture_device_launcher.cc", "video_capture_device_launcher.h", - "video_capture_service.h", "visibility.h", "vpn_service_proxy.h", "web_contents.cc", @@ -399,7 +398,6 @@ jumbo_source_set("browser_sources") { "//services/resource_coordinator/public/cpp:resource_coordinator_cpp", "//services/service_manager/public/cpp", "//services/tracing/public/cpp", - "//services/video_capture/public/mojom", "//services/viz/public/interfaces", "//third_party/webrtc/modules/desktop_capture", diff --git a/chromium/content/public/browser/chromeos/delegate_to_browser_gpu_service_accelerator_factory.h b/chromium/content/public/browser/chromeos/delegate_to_browser_gpu_service_accelerator_factory.h index 2f1f3a43933..46f10decfd6 100644 --- a/chromium/content/public/browser/chromeos/delegate_to_browser_gpu_service_accelerator_factory.h +++ b/chromium/content/public/browser/chromeos/delegate_to_browser_gpu_service_accelerator_factory.h @@ -6,7 +6,7 @@ #define CONTENT_PUBLIC_BROWSER_CHROMEOS_DELEGATE_TO_BROWSER_GPU_SERVICE_ACCELERATOR_FACTORY_H_ #include "content/common/content_export.h" -#include "services/video_capture/public/mojom/video_capture_service.mojom.h" +#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" namespace content { diff --git a/chromium/content/public/browser/service_process_host.cc b/chromium/content/public/browser/service_process_host.cc index e03e0f4dc17..c4ce6643e1b 100644 --- a/chromium/content/public/browser/service_process_host.cc +++ b/chromium/content/public/browser/service_process_host.cc @@ -3,8 +3,6 @@ // found in the LICENSE file. #include "content/public/browser/service_process_host.h" - -#include "base/strings/utf_string_conversions.h" #include "content/public/common/content_client.h" namespace content { @@ -22,12 +20,6 @@ ServiceProcessHost::Options& ServiceProcessHost::Options::WithSandboxType( } ServiceProcessHost::Options& ServiceProcessHost::Options::WithDisplayName( - const std::string& name) { - display_name = base::UTF8ToUTF16(name); - return *this; -} - -ServiceProcessHost::Options& ServiceProcessHost::Options::WithDisplayName( const base::string16& name) { display_name = name; return *this; @@ -45,13 +37,6 @@ ServiceProcessHost::Options& ServiceProcessHost::Options::WithChildFlags( return *this; } -ServiceProcessHost::Options& -ServiceProcessHost::Options::WithExtraCommandLineSwitches( - std::vector<std::string> switches) { - extra_switches = std::move(switches); - return *this; -} - ServiceProcessHost::Options ServiceProcessHost::Options::Pass() { return std::move(*this); } diff --git a/chromium/content/public/browser/service_process_host.h b/chromium/content/public/browser/service_process_host.h index 25d3b3e3f6e..0f76b8be798 100644 --- a/chromium/content/public/browser/service_process_host.h +++ b/chromium/content/public/browser/service_process_host.h @@ -6,7 +6,6 @@ #define CONTENT_PUBLIC_BROWSER_SERVICE_PROCESS_HOST_H_ #include <memory> -#include <string> #include <utility> #include <vector> @@ -59,7 +58,6 @@ class CONTENT_EXPORT ServiceProcessHost { // Specifies the display name of the service process. This should generally // be a human readable and meaningful application or service name and will // appear in places like the system task viewer. - Options& WithDisplayName(const std::string& name); Options& WithDisplayName(const base::string16& name); Options& WithDisplayName(int resource_id); @@ -67,9 +65,6 @@ class CONTENT_EXPORT ServiceProcessHost { // ChildProcessHost for flag definitions. Options& WithChildFlags(int flags); - // Specifies extra command line switches to append before launch. - Options& WithExtraCommandLineSwitches(std::vector<std::string> switches); - // Passes the contents of this Options object to a newly returned Options // value. This must be called when moving a built Options object into a call // to |Launch()|. @@ -78,7 +73,6 @@ class CONTENT_EXPORT ServiceProcessHost { SandboxType sandbox_type = service_manager::SANDBOX_TYPE_UTILITY; base::string16 display_name; base::Optional<int> child_flags; - std::vector<std::string> extra_switches; }; // An interface which can be implemented and registered/unregistered with diff --git a/chromium/content/public/browser/video_capture_service.h b/chromium/content/public/browser/video_capture_service.h deleted file mode 100644 index 9753d1b67a1..00000000000 --- a/chromium/content/public/browser/video_capture_service.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019 The Chromium Authors. 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_VIDEO_CAPTURE_SERVICE_H_ -#define CONTENT_PUBLIC_BROWSER_VIDEO_CAPTURE_SERVICE_H_ - -#include "content/common/content_export.h" -#include "services/video_capture/public/mojom/video_capture_service.mojom.h" - -namespace content { - -// Acquires a VideoCaptureService interface connected either to an in-process -// instance or an out-of-process instance. If out-of-process, the service -// process is launched lazily as needed and shut down when idle. -// -// This is callable from any thread, though when called from off of the UI -// thread, messages sent on the interface will incur an extra thread hop before -// going to the service. -CONTENT_EXPORT video_capture::mojom::VideoCaptureService& -GetVideoCaptureService(); - -// Provides an override for the reference returned by -// |GetVideoCaptureService()|. Call again with null to cancel the override -// before |service| is destroyed. -CONTENT_EXPORT void OverrideVideoCaptureServiceForTesting( - video_capture::mojom::VideoCaptureService* service); - -} // namespace content - -#endif // CONTENT_PUBLIC_BROWSER_VIDEO_CAPTURE_SERVICE_H_ diff --git a/chromium/content/renderer/loader/resource_dispatcher.cc b/chromium/content/renderer/loader/resource_dispatcher.cc index 03bf4a8b7f5..e12289e5962 100644 --- a/chromium/content/renderer/loader/resource_dispatcher.cc +++ b/chromium/content/renderer/loader/resource_dispatcher.cc @@ -65,10 +65,13 @@ void CheckSchemeForReferrerPolicy(const network::ResourceRequest& request) { net::URLRequest:: CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE) && request.referrer.SchemeIsCryptographic() && + !url::Origin::Create(request.url).opaque() && !IsOriginSecure(request.url)) { LOG(FATAL) << "Trying to send secure referrer for insecure request " << "without an appropriate referrer policy.\n" << "URL = " << request.url << "\n" + << "URL's Origin = " + << url::Origin::Create(request.url).Serialize() << "\n" << "Referrer = " << request.referrer; } } diff --git a/chromium/content/utility/services.cc b/chromium/content/utility/services.cc index 39f3d532518..44e7b19cd5a 100644 --- a/chromium/content/utility/services.cc +++ b/chromium/content/utility/services.cc @@ -6,11 +6,7 @@ #include <utility> -#include "base/no_destructor.h" -#include "base/threading/thread_task_runner_handle.h" #include "content/public/utility/content_utility_client.h" -#include "services/video_capture/public/mojom/video_capture_service.mojom.h" -#include "services/video_capture/video_capture_service_impl.h" namespace content { @@ -29,13 +25,6 @@ void HandleServiceRequestOnIOThread( } void HandleServiceRequestOnMainThread(mojo::GenericPendingReceiver receiver) { - if (auto video_capture_receiver = - receiver.As<video_capture::mojom::VideoCaptureService>()) { - static base::NoDestructor<video_capture::VideoCaptureServiceImpl> service( - std::move(video_capture_receiver), base::ThreadTaskRunnerHandle::Get()); - return; - } - // If the request was handled already, we should not reach this point. DCHECK(receiver.is_valid()); GetContentClient()->utility()->RunMainThreadService(std::move(receiver)); diff --git a/chromium/content/utility/utility_service_factory.cc b/chromium/content/utility/utility_service_factory.cc index e4c8405fb17..e9c336020ea 100644 --- a/chromium/content/utility/utility_service_factory.cc +++ b/chromium/content/utility/utility_service_factory.cc @@ -32,6 +32,8 @@ #include "services/tracing/public/cpp/tracing_features.h" #include "services/tracing/public/mojom/constants.mojom.h" #include "services/tracing/tracing_service.h" +#include "services/video_capture/public/mojom/constants.mojom.h" +#include "services/video_capture/service_impl.h" #if BUILDFLAG(ENABLE_LIBRARY_CDMS) #include "media/cdm/cdm_adapter_factory.h" // nogncheck @@ -166,6 +168,9 @@ void UtilityServiceFactory::RunService( std::move(network_registry_), base::SequencedTaskRunnerHandle::Get())); return; + } else if (service_name == video_capture::mojom::kServiceName) { + service = std::make_unique<video_capture::ServiceImpl>( + std::move(request), base::ThreadTaskRunnerHandle::Get()); } #if BUILDFLAG(ENABLE_LIBRARY_CDMS) else if (service_name == media::mojom::kCdmServiceName) { diff --git a/chromium/extensions/browser/api/media_perception_private/media_perception_api_delegate.h b/chromium/extensions/browser/api/media_perception_private/media_perception_api_delegate.h index 0a82196539d..56e789f4329 100644 --- a/chromium/extensions/browser/api/media_perception_private/media_perception_api_delegate.h +++ b/chromium/extensions/browser/api/media_perception_private/media_perception_api_delegate.h @@ -11,8 +11,8 @@ #include "base/files/file_path.h" #include "chromeos/services/media_perception/public/mojom/media_perception_service.mojom.h" #include "extensions/common/api/media_perception_private.h" -#include "mojo/public/cpp/bindings/pending_receiver.h" -#include "services/video_capture/public/mojom/video_source_provider.mojom.h" +#include "services/video_capture/public/mojom/device_factory.mojom.h" +#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" namespace content { @@ -40,12 +40,12 @@ class MediaPerceptionAPIDelegate { const api::media_perception_private::ComponentType& type, LoadCrOSComponentCallback load_callback) = 0; - // Provides an interface to the Video Capture service (started lazily by the - // browser) to connect the MediaPerceptionService to it and establish a direct - // Mojo IPC-based connection. - virtual void BindVideoSourceProvider( - mojo::PendingReceiver<video_capture::mojom::VideoSourceProvider> - receiver) = 0; + // Provides an interface to the VideoCaptureService (started lazily by the + // Chrome service manager) to connect the MediaPerceptionService to it and + // establish a direct Mojo IPC-based connection. + // |provider| is owned by the caller. + virtual void BindDeviceFactoryProviderToVideoCaptureService( + video_capture::mojom::DeviceFactoryProviderPtr* provider) = 0; // Provides an interface to set a handler for an incoming // MediaPerceptionRequest. diff --git a/chromium/extensions/browser/api/media_perception_private/media_perception_api_manager.cc b/chromium/extensions/browser/api/media_perception_private/media_perception_api_manager.cc index f73afc87c24..e555a53d175 100644 --- a/chromium/extensions/browser/api/media_perception_private/media_perception_api_manager.cc +++ b/chromium/extensions/browser/api/media_perception_private/media_perception_api_manager.cc @@ -11,6 +11,8 @@ #include "base/bind.h" #include "base/files/file_path.h" #include "base/lazy_instance.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/media_analytics/media_analytics_client.h" #include "chromeos/dbus/upstart/upstart_client.h" @@ -23,11 +25,14 @@ #include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/system/invitation.h" +#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" namespace extensions { namespace { +const int kStartupDelayMs = 1000; + extensions::api::media_perception_private::State GetStateForServiceError( const extensions::api::media_perception_private::ServiceError service_error) { @@ -96,7 +101,9 @@ class MediaPerceptionAPIManager::MediaPerceptionControllerClient void ConnectToVideoCaptureService( video_capture::mojom::VideoSourceProviderRequest request) override { DCHECK(delegate_) << "Delegate not set."; - delegate_->BindVideoSourceProvider(std::move(request)); + delegate_->BindDeviceFactoryProviderToVideoCaptureService( + &device_factory_provider_); + device_factory_provider_->ConnectToVideoSourceProvider(std::move(request)); } private: @@ -108,6 +115,10 @@ class MediaPerceptionAPIManager::MediaPerceptionControllerClient chromeos::media_perception::mojom::MediaPerceptionControllerClient> binding_; + // Bound to the VideoCaptureService to establish the connection to the + // media analytics process. + video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider_; + DISALLOW_COPY_AND_ASSIGN(MediaPerceptionControllerClient); }; @@ -373,7 +384,14 @@ void MediaPerceptionAPIManager::UpstartStartProcessCallback( return; } - SendMojoInvitation(std::move(callback)); + // TODO(crbug.com/1003968): Look into using + // ObjectProxy::WaitForServiceToBeAvailable instead, since a timeout is + // inherently not deterministic, even if it works in practice. + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::BindOnce(&MediaPerceptionAPIManager::SendMojoInvitation, + weak_ptr_factory_.GetWeakPtr(), std::move(callback)), + base::TimeDelta::FromMilliseconds(kStartupDelayMs)); } void MediaPerceptionAPIManager::SendMojoInvitation( diff --git a/chromium/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc b/chromium/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc index 76ecfa5b798..51c3644bc63 100644 --- a/chromium/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc +++ b/chromium/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc @@ -48,9 +48,8 @@ class TestMediaPerceptionAPIDelegate : public MediaPerceptionAPIDelegate { base::BindOnce(std::move(load_callback), false, base::FilePath())); } - void BindVideoSourceProvider( - mojo::PendingReceiver<video_capture::mojom::VideoSourceProvider> receiver) - override { + void BindDeviceFactoryProviderToVideoCaptureService( + video_capture::mojom::DeviceFactoryProviderPtr* provider) override { NOTIMPLEMENTED(); } diff --git a/chromium/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc b/chromium/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc index 7dade372570..e5f9ae51104 100644 --- a/chromium/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc +++ b/chromium/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc @@ -419,6 +419,11 @@ ui::AXTree::Selection AutomationAXTreeWrapper::GetUnignoredSelection() { return unignored_selection; } +ui::AXNode* AutomationAXTreeWrapper::GetUnignoredNodeFromId(int32_t id) { + ui::AXNode* node = tree_.GetFromId(id); + return (node && !node->IsIgnored()) ? node : nullptr; +} + // static std::map<ui::AXTreeID, AutomationAXTreeWrapper*>& AutomationAXTreeWrapper::GetChildTreeIDReverseMap() { @@ -427,7 +432,7 @@ AutomationAXTreeWrapper::GetChildTreeIDReverseMap() { return *child_tree_id_reverse_map; } -void AutomationAXTreeWrapper::OnNodeDataWillChange( +void AutomationAXTreeWrapper::OnNodeDataChanged( ui::AXTree* tree, const ui::AXNodeData& old_node_data, const ui::AXNodeData& new_node_data) { diff --git a/chromium/extensions/renderer/api/automation/automation_ax_tree_wrapper.h b/chromium/extensions/renderer/api/automation/automation_ax_tree_wrapper.h index 1065abf90e0..c0cb83337ef 100644 --- a/chromium/extensions/renderer/api/automation/automation_ax_tree_wrapper.h +++ b/chromium/extensions/renderer/api/automation/automation_ax_tree_wrapper.h @@ -27,6 +27,9 @@ class AutomationAXTreeWrapper : public ui::AXTreeObserver { // child trees, if any. static AutomationAXTreeWrapper* GetParentOfTreeId(ui::AXTreeID tree_id); + static std::map<ui::AXTreeID, AutomationAXTreeWrapper*>& + GetChildTreeIDReverseMap(); + ui::AXTreeID tree_id() const { return tree_id_; } ui::AXTree* tree() { return &tree_; } AutomationInternalCustomBindings* owner() { return owner_; } @@ -49,14 +52,15 @@ class AutomationAXTreeWrapper : public ui::AXTreeObserver { ui::AXTree::Selection GetUnignoredSelection(); - static std::map<ui::AXTreeID, AutomationAXTreeWrapper*>& - GetChildTreeIDReverseMap(); + // Returns an AXNode from the underlying tree if it both exists and is not + // ignored. + ui::AXNode* GetUnignoredNodeFromId(int32_t id); private: // AXTreeObserver overrides. - void OnNodeDataWillChange(ui::AXTree* tree, - const ui::AXNodeData& old_node_data, - const ui::AXNodeData& new_node_data) override; + void OnNodeDataChanged(ui::AXTree* tree, + const ui::AXNodeData& old_node_data, + const ui::AXNodeData& new_node_data) override; void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override; void OnAtomicUpdateFinished(ui::AXTree* tree, bool root_changed, diff --git a/chromium/extensions/renderer/api/automation/automation_internal_custom_bindings.cc b/chromium/extensions/renderer/api/automation/automation_internal_custom_bindings.cc index 91d70dff48e..98111cb0fdb 100644 --- a/chromium/extensions/renderer/api/automation/automation_internal_custom_bindings.cc +++ b/chromium/extensions/renderer/api/automation/automation_internal_custom_bindings.cc @@ -200,7 +200,7 @@ class NodeIDWrapper : public base::RefCountedThreadSafe<NodeIDWrapper> { if (!tree_wrapper) return; - ui::AXNode* node = tree_wrapper->tree()->GetFromId(node_id); + ui::AXNode* node = tree_wrapper->GetUnignoredNodeFromId(node_id); if (!node) return; @@ -256,7 +256,7 @@ class NodeIDPlusAttributeWrapper if (!tree_wrapper) return; - ui::AXNode* node = tree_wrapper->tree()->GetFromId(node_id); + ui::AXNode* node = tree_wrapper->GetUnignoredNodeFromId(node_id); if (!node) return; @@ -314,7 +314,7 @@ class NodeIDPlusRangeWrapper if (!tree_wrapper) return; - ui::AXNode* node = tree_wrapper->tree()->GetFromId(node_id); + ui::AXNode* node = tree_wrapper->GetUnignoredNodeFromId(node_id); if (!node) return; @@ -366,7 +366,7 @@ class NodeIDPlusStringBoolWrapper if (!tree_wrapper) return; - ui::AXNode* node = tree_wrapper->tree()->GetFromId(node_id); + ui::AXNode* node = tree_wrapper->GetUnignoredNodeFromId(node_id); if (!node) return; @@ -422,7 +422,7 @@ class NodeIDPlusDimensionsWrapper if (!tree_wrapper) return; - ui::AXNode* node = tree_wrapper->tree()->GetFromId(node_id); + ui::AXNode* node = tree_wrapper->GetUnignoredNodeFromId(node_id); if (!node) return; diff --git a/chromium/gpu/config/gpu_driver_bug_list.json b/chromium/gpu/config/gpu_driver_bug_list.json index 3757a5cfc7a..e88ecea2445 100644 --- a/chromium/gpu/config/gpu_driver_bug_list.json +++ b/chromium/gpu/config/gpu_driver_bug_list.json @@ -3305,6 +3305,19 @@ "features": [ "disable_delayed_copy_nv12" ] + }, + { + "id": 310, + "description": "Context lost recovery often fails on Mali on CrOS.", + "cr_bugs": [992286], + "os": { + "type": "chromeos" + }, + "gl_vendor": "ARM.*", + "gl_renderer": ".*Mali-.*", + "features": [ + "exit_on_context_lost" + ] } ] } diff --git a/chromium/gpu/config/gpu_lists_version.h b/chromium/gpu/config/gpu_lists_version.h index e45237ad52f..8a83655f9c0 100644 --- a/chromium/gpu/config/gpu_lists_version.h +++ b/chromium/gpu/config/gpu_lists_version.h @@ -3,6 +3,6 @@ #ifndef GPU_CONFIG_GPU_LISTS_VERSION_H_ #define GPU_CONFIG_GPU_LISTS_VERSION_H_ -#define GPU_LISTS_VERSION "f7668708a327d2ab897cd802bbbc8a8b67283ec8" +#define GPU_LISTS_VERSION "89700f0d311d189a766a3532c1e6de2c94d429f9" #endif // GPU_CONFIG_GPU_LISTS_VERSION_H_ diff --git a/chromium/infra/config/cr-buildbucket.cfg b/chromium/infra/config/cr-buildbucket.cfg index 3d517cb9fe9..daa2b73196f 100644 --- a/chromium/infra/config/cr-buildbucket.cfg +++ b/chromium/infra/config/cr-buildbucket.cfg @@ -455,7 +455,7 @@ builder_mixins { name: "chromeos-ci" # All CrOS building is done via cross-compilation on linux hosts, so use the # linux mixin. - mixins: "linux" + mixins: "linux-xenial" recipe { properties: "mastername:chromium.chromiumos" } @@ -465,7 +465,7 @@ builder_mixins { name: "chromeos-try" # All CrOS building is done via cross-compilation on linux hosts, so use the # linux mixin. - mixins: "linux" + mixins: "linux-xenial" recipe { properties: "mastername:tryserver.chromium.chromiumos" } @@ -567,7 +567,7 @@ builder_mixins { } builder_mixins { - name: "linux" + name: "linux-trusty" dimensions: "os:Ubuntu-14.04" } @@ -629,7 +629,7 @@ builder_mixins { builder_mixins { name: "linux-try" - mixins: "linux" + mixins: "linux-xenial" recipe { properties: "mastername:tryserver.chromium.linux" } @@ -660,7 +660,7 @@ builder_mixins { builder_mixins { name: "linux-ci" - mixins: "linux" + mixins: "linux-xenial" mixins: "goma-many-jobs-for-ci" recipe { properties: "mastername:chromium.linux" @@ -670,7 +670,7 @@ builder_mixins { # Counterpart of "linux-ci" that uses Goma RBE Prod server. builder_mixins { name: "linux-ci-goma-rbe-prod" - mixins: "linux" + mixins: "linux-xenial" mixins: "goma-rbe-prod-many-jobs-for-ci" recipe { properties: "mastername:chromium.linux" @@ -1141,7 +1141,6 @@ buckets { builders { name: "android-code-coverage" mixins: "code-coverage" - mixins: "linux" mixins: "linux-xenial" mixins: "builderless" dimensions: "cores:32" @@ -1351,28 +1350,24 @@ buckets { builders { name: "chromeos-amd64-generic-asan-rel" mixins: "chromeos-ci" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "chromeos-amd64-generic-cfi-thin-lto-rel" mixins: "chromeos-ci" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "chromeos-amd64-generic-rel" mixins: "chromeos-ci" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "chromeos-amd64-generic-rel-vm-tests" mixins: "fyi-ci" - mixins: "linux" mixins: "linux-xenial" mixins: "builderless" } @@ -1380,7 +1375,6 @@ buckets { builders { name: "chromeos-arm-generic-rel" mixins: "chromeos-ci" - mixins: "linux-xenial" mixins: "builderless" } @@ -1388,7 +1382,6 @@ buckets { name: "chromeos-vm-code-coverage" mixins: "code-coverage" mixins: "fyi-ci" - mixins: "linux" mixins: "linux-xenial" mixins: "builderless" dimensions: "cores:32" @@ -1398,7 +1391,6 @@ buckets { builders { name: "chromeos-kevin-rel-hw-tests" mixins: "fyi-ci" - mixins: "linux" mixins: "linux-xenial" mixins: "builderless" } @@ -1406,7 +1398,6 @@ buckets { builders { name: "chromeos-kevin-rel" mixins: "chromeos-ci" - mixins: "linux-xenial" mixins: "builderless" } @@ -1414,7 +1405,6 @@ buckets { name: "linux-chromeos-code-coverage" mixins: "code-coverage" mixins: "fyi-ci" - mixins: "linux" mixins: "linux-xenial" mixins: "builderless" dimensions: "cores:32" @@ -1424,7 +1414,6 @@ buckets { builders { name: "linux-chromeos-dbg" mixins: "chromeos-ci" - mixins: "linux-xenial" mixins: "builderless" } @@ -1432,7 +1421,6 @@ buckets { name: "linux-chromeos-oobe-code-coverage" mixins: "code-coverage" mixins: "fyi-ci" - mixins: "linux" mixins: "linux-xenial" mixins: "builderless" dimensions: "cores:32" @@ -1445,14 +1433,12 @@ buckets { builders { name: "linux-chromeos-rel" mixins: "chromeos-ci" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "Linux ChromiumOS Full" mixins: "chromeos-ci" - mixins: "linux-xenial" mixins: "builderless" } @@ -1460,28 +1446,24 @@ buckets { builders { name: "Fuchsia ARM64" mixins: "linux-ci" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "fuchsia-arm64-cast" mixins: "linux-ci" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "Fuchsia x64" mixins: "linux-ci" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "fuchsia-x64-cast" mixins: "linux-ci" - mixins: "linux-xenial" mixins: "builderless" } @@ -1510,7 +1492,6 @@ buckets { builders { name: "Cast Audio Linux" mixins: "linux-ci" - mixins: "linux-xenial" mixins: "builderless" dimensions: "ssd:1" } @@ -1518,7 +1499,6 @@ buckets { builders { name: "Cast Linux" mixins: "linux-ci" - mixins: "linux-xenial" mixins: "builderless" recipe { properties_j: "$build/goma:{\"jobs\": 50}" @@ -1530,7 +1510,6 @@ buckets { mixins: "deterministic" mixins: "linux-ci-goma-rbe-prod" mixins: "builderless" - mixins: "linux-xenial" } builders { @@ -1538,7 +1517,6 @@ buckets { mixins: "deterministic" mixins: "linux-ci-goma-rbe-prod" mixins: "builderless" - mixins: "linux-xenial" # This builder does local build, so needs large number of cores. dimensions: "cores:32" @@ -1547,13 +1525,11 @@ buckets { builders { name: "Leak Detection Linux" mixins: "linux-ci-goma-rbe-prod" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "Linux ASan LSan Builder" - mixins: "linux" mixins: "memory-ci" mixins: "linux-xenial" mixins: "builderless" @@ -1562,7 +1538,6 @@ buckets { builders { name: "Linux ASan LSan Tests (1)" - mixins: "linux" mixins: "memory-ci" mixins: "linux-xenial" mixins: "builderless" @@ -1570,7 +1545,6 @@ buckets { builders { name: "Linux ASan Tests (sandboxed)" - mixins: "linux" mixins: "memory-ci" mixins: "linux-xenial" mixins: "builderless" @@ -1580,19 +1554,18 @@ buckets { name: "linux-trusty-rel" mixins: "linux-ci-goma-rbe-prod" mixins: "builderless" + mixins: "linux-trusty" } builders { name: "Linux Builder" mixins: "linux-ci-goma-rbe-prod" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "Linux Tests" mixins: "linux-ci" - mixins: "linux-xenial" mixins: "builderless" } @@ -1606,7 +1579,6 @@ buckets { builders { name: "linux-blink-heap-concurrent-marking-tsan-rel" mixins: "fyi-ci" - mixins: "linux" mixins: "builderless" mixins: "linux-xenial" } @@ -1614,7 +1586,6 @@ buckets { builders { name: "linux-blink-heap-verification" mixins: "fyi-ci" - mixins: "linux" mixins: "linux-xenial" mixins: "builderless" } @@ -1622,7 +1593,6 @@ buckets { builders { name: "linux-blink-heap-unified-gc" mixins: "fyi-ci" - mixins: "linux" mixins: "linux-xenial" mixins: "builderless" } @@ -1630,7 +1600,6 @@ buckets { builders { name: "linux_chromium_component_updater" mixins: "linux-ci" - mixins: "linux-xenial" mixins: "builderless" recipe { name: "findit/chromium/update_components" @@ -1641,7 +1610,6 @@ buckets { builders { name: "linux-chromium-tests-staging-builder" mixins: "fyi-ci" - mixins: "linux" mixins: "linux-xenial" mixins: "builderless" } @@ -1649,7 +1617,6 @@ buckets { builders { name: "linux-chromium-tests-staging-tests" mixins: "fyi-ci" - mixins: "linux" mixins: "linux-xenial" mixins: "builderless" } @@ -1657,21 +1624,18 @@ buckets { builders { name: "linux-gcc-rel" mixins: "linux-ci" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "linux-jumbo-rel" mixins: "linux-ci" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "linux-ozone-rel" mixins: "linux-ci" - mixins: "linux-xenial" mixins: "builderless" } @@ -1693,27 +1657,23 @@ buckets { builders { name: "Linux Builder (dbg)" mixins: "linux-ci-goma-rbe-prod" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "Linux Builder (dbg)(32)" mixins: "linux-ci-goma-rbe-prod" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "Linux Tests (dbg)(1)" mixins: "linux-ci" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "Linux TSan Builder" - mixins: "linux" mixins: "linux-xenial" mixins: "memory-ci" mixins: "builderless" @@ -1721,7 +1681,6 @@ buckets { builders { name: "Linux TSan Tests" - mixins: "linux" mixins: "linux-xenial" mixins: "memory-ci" mixins: "builderless" @@ -3901,48 +3860,40 @@ buckets { builders { mixins: "chromeos-try" name: "chromeos-amd64-generic-cfi-thin-lto-rel" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "chromeos-try" - mixins: "linux-xenial" mixins: "builderless" name: "chromeos-amd64-generic-rel" } builders { mixins: "chromeos-try" - mixins: "linux-xenial" mixins: "builderless" name: "chromeos-arm-generic-rel" } builders { mixins: "chromeos-try" name: "chromeos-kevin-compile-rel" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "chromeos-try" name: "chromeos-kevin-rel" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "chromeos-try" - mixins: "linux-xenial" mixins: "builderless" name: "linux-chromeos-compile-dbg" } builders { mixins: "chromeos-try" - mixins: "linux-xenial" mixins: "builderless" name: "linux-chromeos-dbg" } builders { mixins: "chromeos-try" - mixins: "linux-xenial" mixins: "goma-j150" mixins: "builderless" name: "linux-chromeos-rel" @@ -3951,18 +3902,15 @@ buckets { builders { mixins: "linux-try" name: "cast_shell_audio_linux" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" name: "cast_shell_linux" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" - mixins: "linux-xenial" name: "chromium_presubmit" mixins: "builderless" recipe { @@ -3982,7 +3930,6 @@ buckets { recipe { name: "closure_compilation" } - mixins: "linux-xenial" mixins: "builderless" } @@ -3991,43 +3938,36 @@ buckets { builders { mixins: "linux-try" name: "fuchsia_arm64" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "builderless" mixins: "linux-try" - mixins: "linux-xenial" name: "fuchsia-arm64-cast" } builders { mixins: "builderless" mixins: "linux-try" - mixins: "linux-xenial" name: "fuchsia-fyi-arm64-rel" } builders { mixins: "builderless" mixins: "linux-try" - mixins: "linux-xenial" name: "fuchsia-fyi-x64-dbg" } builders { mixins: "builderless" mixins: "linux-try" - mixins: "linux-xenial" name: "fuchsia-fyi-x64-rel" } builders { mixins: "linux-try" name: "fuchsia_x64" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "builderless" mixins: "linux-try" - mixins: "linux-xenial" name: "fuchsia-x64-cast" } builders { @@ -4075,7 +4015,6 @@ buckets { mixins: "linux-try" mixins: "goma-rbe-prod" name: "leak_detection_linux" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-angle-try" name: "fuchsia-angle-rel" } @@ -4094,38 +4033,32 @@ buckets { builders { mixins: "builderless" mixins: "linux-try" - mixins: "linux-xenial" name: "linux-blink-heap-concurrent-marking-tsan-rel" } builders { mixins: "linux-try" name: "linux-blink-heap-verification-try" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" mixins: "goma-rbe-prod" name: "linux-dcheck-off-rel" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" name: "linux-gcc-rel" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" name: "linux-jumbo-rel" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "linux-libfuzzer-asan-rel" mixins: "linux-try" - mixins: "linux-xenial" mixins: "builderless" recipe { name: "chromium_libfuzzer_trybot" @@ -4135,14 +4068,12 @@ buckets { mixins: "builderless" mixins: "linux-try" name: "linux-ozone-rel" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" mixins: "goma-rbe-prod" name: "linux-webkit-msan-rel" - mixins: "linux-xenial" mixins: "builderless" } builders { @@ -4150,24 +4081,20 @@ buckets { # TODO(crbug.com/986191): re-enable RBE+ATS when the issue is fixed. # mixins: "goma-rbe-prod-ats" name: "linux_arm" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" name: "linux_chromium_analysis" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" name: "linux_chromium_archive_rel_ng" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" - mixins: "linux-xenial" mixins: "goma-j150" name: "linux_chromium_asan_rel_ng" mixins: "builderless" @@ -4176,7 +4103,6 @@ buckets { builders { mixins: "linux-try" mixins: "goma-rbe-prod" - mixins: "linux-xenial" mixins: "builderless" name: "linux_chromium_cfi_rel_ng" dimensions: "cores:32" @@ -4185,40 +4111,34 @@ buckets { mixins: "linux-try" mixins: "goma-j150" name: "linux_chromium_chromeos_asan_rel_ng" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" mixins: "goma-j150" name: "linux_chromium_chromeos_msan_rel_ng" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "linux_chromium_clobber_deterministic", mixins: "linux-try" mixins: "deterministic" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" name: "linux_chromium_clobber_rel_ng" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" name: "linux_chromium_compile_dbg_32_ng" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" mixins: "linux-debug-cache" mixins: "goma-j150" - mixins: "linux-xenial" mixins: "builderless" name: "linux_chromium_compile_dbg_ng" } @@ -4226,7 +4146,6 @@ buckets { mixins: "linux-try" mixins: "goma-rbe-prod" name: "linux_chromium_compile_rel_ng" - mixins: "linux-xenial" mixins: "builderless" } builders { @@ -4234,26 +4153,22 @@ buckets { mixins: "goma-rbe-prod" mixins: "linux-debug-cache" mixins: "linux-try" - mixins: "linux-xenial" name: "linux_chromium_dbg_ng" } builders { mixins: "linux-try" mixins: "goma-rbe-prod-j150" name: "linux_chromium_msan_rel_ng" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" mixins: "goma-j150" name: "linux-coverage-rel" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" - mixins: "linux-xenial" mixins: "goma-j150" mixins: "builderless" name: "linux-rel" @@ -4262,11 +4177,11 @@ buckets { mixins: "linux-try" mixins: "goma-rbe-prod-j150" mixins: "builderless" + mixins: "linux-trusty" name: "linux-trusty-rel" } builders { mixins: "linux-try" - mixins: "linux-xenial" mixins: "goma-j150" mixins: "builderless" name: "linux_chromium_tsan_rel_ng" @@ -4274,7 +4189,6 @@ buckets { builders { mixins: "linux-try" name: "linux_chromium_ubsan_rel_ng" - mixins: "linux-xenial" mixins: "builderless" } builders { @@ -4285,32 +4199,29 @@ buckets { builders { mixins: "linux-try" name: "linux_layout_tests_composite_after_paint" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" name: "linux_layout_tests_layout_ng" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" mixins: "goma-rbe-prod" name: "linux_mojo" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-try" name: "linux_mojo_chromeos" - mixins: "linux-xenial" mixins: "builderless" } builders { mixins: "linux-optional-gpu-try" name: "linux_optional_gpu_tests_rel" } builders { mixins: "linux-try" mixins: "upload_clang" + mixins: "linux-trusty" name: "linux_upload_clang" dimensions: "cores:32" } @@ -4591,14 +4502,12 @@ buckets { builders { name: "linux-annotator-rel" mixins: "linux-try" - mixins: "linux-xenial" mixins: "builderless" } builders { name: "linux_vr" mixins: "linux-try" mixins: "goma-rbe-prod" - mixins: "linux-xenial" mixins: "builderless" } builders { @@ -4630,7 +4539,6 @@ buckets { name: "layout_test_leak_detection" mixins: "linux-try" mixins: "goma-rbe-prod" - mixins: "linux-xenial" mixins: "builderless" } builders { @@ -4652,7 +4560,6 @@ buckets { # Blink try builders. builders { name: "linux-blink-rel" - mixins: "linux" mixins: "blink-try" mixins: "linux-xenial" mixins: "builderless" @@ -4749,7 +4656,7 @@ buckets { } builders { name: "WebRTC Chromium Linux Tester" - mixins: "linux" + mixins: "linux-trusty" } # Mac diff --git a/chromium/ios/chrome/browser/ui/dialogs/BUILD.gn b/chromium/ios/chrome/browser/ui/dialogs/BUILD.gn index 1ae4f03ca40..ed148d8a047 100644 --- a/chromium/ios/chrome/browser/ui/dialogs/BUILD.gn +++ b/chromium/ios/chrome/browser/ui/dialogs/BUILD.gn @@ -52,6 +52,8 @@ source_set("dialogs_internal") { sources = [ "dialog_presenter.h", "dialog_presenter.mm", + "java_script_dialog_metrics.cc", + "java_script_dialog_metrics.h", "java_script_dialog_presenter_impl.h", "java_script_dialog_presenter_impl.mm", "nsurl_protection_space_util.h", diff --git a/chromium/ios/chrome/test/earl_grey/BUILD.gn b/chromium/ios/chrome/test/earl_grey/BUILD.gn index 79ec78648ee..9eb1ac2efc8 100644 --- a/chromium/ios/chrome/test/earl_grey/BUILD.gn +++ b/chromium/ios/chrome/test/earl_grey/BUILD.gn @@ -254,6 +254,7 @@ source_set("test_support") { "//ios/chrome/browser/ui/tab_grid/grid:grid_ui_constants", "//ios/chrome/browser/ui/table_view/cells", "//ios/chrome/browser/ui/toolbar/buttons", + "//ios/chrome/browser/ui/toolbar/keyboard_assist", "//ios/chrome/browser/ui/toolbar/public", "//ios/chrome/browser/ui/util", "//ios/chrome/test/app:test_support", @@ -351,6 +352,7 @@ source_set("eg_app_support+eg2") { "//ios/chrome/browser/ui/static_content", "//ios/chrome/browser/ui/tab_grid:tab_grid_ui_constants", "//ios/chrome/browser/ui/tab_grid/grid:grid_ui_constants", + "//ios/chrome/browser/ui/toolbar/keyboard_assist", "//ios/chrome/browser/ui/toolbar/public", "//ios/chrome/browser/ui/util", "//ios/chrome/test/app:test_support", diff --git a/chromium/media/blink/webmediaplayer_impl.cc b/chromium/media/blink/webmediaplayer_impl.cc index 17c6dbe0452..0fe502b37df 100644 --- a/chromium/media/blink/webmediaplayer_impl.cc +++ b/chromium/media/blink/webmediaplayer_impl.cc @@ -400,8 +400,9 @@ WebMediaPlayerImpl::WebMediaPlayerImpl( media_metrics_provider_->SetIsAdMedia(); #if defined(OS_ANDROID) - renderer_factory_selector_->SetRemotePlayStateChangeCB(base::BindRepeating( - &WebMediaPlayerImpl::OnRemotePlayStateChange, weak_this_)); + renderer_factory_selector_->SetRemotePlayStateChangeCB( + BindToCurrentLoop(base::BindRepeating( + &WebMediaPlayerImpl::OnRemotePlayStateChange, weak_this_))); #endif // defined (OS_ANDROID) } @@ -2466,6 +2467,7 @@ void WebMediaPlayerImpl::FlingingStopped() { void WebMediaPlayerImpl::OnRemotePlayStateChange(MediaStatus::State state) { DCHECK(is_flinging_); + DCHECK(main_task_runner_->BelongsToCurrentThread()); if (state == MediaStatus::State::PLAYING && Paused()) { DVLOG(1) << __func__ << " requesting PLAY."; diff --git a/chromium/media/gpu/vaapi/vp8_encoder.cc b/chromium/media/gpu/vaapi/vp8_encoder.cc index fa1212f52fb..c2b4f03f753 100644 --- a/chromium/media/gpu/vaapi/vp8_encoder.cc +++ b/chromium/media/gpu/vaapi/vp8_encoder.cc @@ -162,6 +162,12 @@ void VP8Encoder::InitializeFrameHeader() { // TODO(sprang): Make this dynamic. Value based on reference implementation // in libyami (https://github.com/intel/libyami). current_frame_hdr_.loopfilter_hdr.level = 19; + + // b/138840822: Set mb_no_skip_coeff and loop_filter_adj_enable to 1 as a + // workaround of color artifacts issue with a kepler device hw decoder and + // ffmpeg sw decoder. + current_frame_hdr_.mb_no_skip_coeff = 1; + current_frame_hdr_.loopfilter_hdr.loop_filter_adj_enable = 1; } void VP8Encoder::UpdateFrameHeader(bool keyframe) { diff --git a/chromium/media/mojo/services/mojo_cdm_service.cc b/chromium/media/mojo/services/mojo_cdm_service.cc index 3334ac0f6d3..a49ac813b0f 100644 --- a/chromium/media/mojo/services/mojo_cdm_service.cc +++ b/chromium/media/mojo/services/mojo_cdm_service.cc @@ -60,7 +60,9 @@ void MojoCdmService::Initialize(const std::string& key_system, const CdmConfig& cdm_config, InitializeCallback callback) { DVLOG(1) << __func__ << ": " << key_system; - DCHECK(!cdm_); + + CHECK(!has_initialize_been_called_) << "Initialize should only happen once"; + has_initialize_been_called_ = true; auto weak_this = weak_factory_.GetWeakPtr(); cdm_factory_->Create( @@ -154,6 +156,7 @@ void MojoCdmService::OnCdmCreated( return; } + CHECK(!cdm_) << "CDM should only be created once."; cdm_ = cdm; if (context_) { diff --git a/chromium/media/mojo/services/mojo_cdm_service.h b/chromium/media/mojo/services/mojo_cdm_service.h index 09e00de20e4..71d5b593b83 100644 --- a/chromium/media/mojo/services/mojo_cdm_service.h +++ b/chromium/media/mojo/services/mojo_cdm_service.h @@ -101,6 +101,8 @@ class MEDIA_MOJO_EXPORT MojoCdmService : public mojom::ContentDecryptionModule { // Callback for when |decryptor_| loses connectivity. void OnDecryptorConnectionError(); + bool has_initialize_been_called_ = false; + CdmFactory* cdm_factory_; MojoCdmServiceContext* const context_ = nullptr; scoped_refptr<::media::ContentDecryptionModule> cdm_; diff --git a/chromium/services/content/public/cpp/navigable_contents_view.cc b/chromium/services/content/public/cpp/navigable_contents_view.cc index 3c2036fa3f2..b2a150a9380 100644 --- a/chromium/services/content/public/cpp/navigable_contents_view.cc +++ b/chromium/services/content/public/cpp/navigable_contents_view.cc @@ -22,8 +22,6 @@ #endif // defined(TOOLKIT_VIEWS) #if defined(USE_AURA) -#include "ui/aura/client/focus_change_observer.h" // nogncheck -#include "ui/aura/client/focus_client.h" // nogncheck #include "ui/aura/layout_manager.h" // nogncheck #include "ui/aura/window.h" // nogncheck #endif @@ -77,25 +75,14 @@ class LocalWindowLayoutManager : public aura::LayoutManager { // Owns an Aura window which parents another Aura window in the same process, // corresponding to a web contents view hosted in the process. -class LocalViewHost : public views::NativeViewHost, - public aura::WindowObserver, - public aura::client::FocusChangeObserver { +class LocalViewHost : public views::NativeViewHost { public: LocalViewHost(aura::Window* window, NavigableContents* contents) : window_(window), contents_(contents) { window_->SetLayoutManager(new LocalWindowLayoutManager(window_)); - window_->AddObserver(this); - - // We set focus behavior to |ALWAYS| to ensure that we receive window focus - // change events when our hosted WebContents takes focus. We utilize this - // change event to keep LocalViewHost and WebContents focus state in sync. - SetFocusBehavior(FocusBehavior::ALWAYS); } - ~LocalViewHost() override { - if (!window_destroyed_) - window_->RemoveObserver(this); - } + ~LocalViewHost() override = default; // views::View: void AddedToWidget() override { @@ -116,54 +103,10 @@ class LocalViewHost : public views::NativeViewHost, } } - // aura::WindowObserver: - void OnWindowAddedToRootWindow(aura::Window* window) override { - // Once our |window_| has been added to its root window, we can obtain a - // reference to the root window's focus client which we observe to detect - // window focus changed events. - auto* focus_client = aura::client::GetFocusClient(window_); - if (focus_client) - focus_client->AddObserver(this); - } - - void OnWindowRemovingFromRootWindow(aura::Window* window, - aura::Window* root_window) override { - // We need to stop observing the root window's focus client before our - // |window_| is removed. Otherwise, we will leak a pointer to LocalViewHost - // to the focus client that will persist even after LocalViewHost is - // destroyed. This will cause a crash when the focus client attempts to - // notify our destroyed instance of focus changed events. - auto* focus_client = aura::client::GetFocusClient(window_); - if (focus_client) - focus_client->RemoveObserver(this); - } - - void OnWindowDestroying(aura::Window* window) override { - window_->RemoveObserver(this); - window_destroyed_ = true; - } - - // aura::client::FocusChangeObserver: - void OnWindowFocused(aura::Window* gained_focus, - aura::Window* lost_focus) override { - // We need to ensure that LocalViewHost's focus state is synced with that of - // |window_|. This only needs to be done when gaining focus and when - // LocalViewHost doesn't already have focus. - if (!gained_focus || HasFocus()) - return; - - // When the window gaining focus is contained within |window_|, we need to - // request focus on LocalHostView to remain in sync. - if (window_->Contains(gained_focus)) - RequestFocus(); - } - private: aura::Window* const window_; NavigableContents* const contents_; - bool window_destroyed_ = false; - DISALLOW_COPY_AND_ASSIGN(LocalViewHost); }; diff --git a/chromium/services/network/mdns_responder.cc b/chromium/services/network/mdns_responder.cc index a977bb62a87..63569ae6c18 100644 --- a/chromium/services/network/mdns_responder.cc +++ b/chromium/services/network/mdns_responder.cc @@ -36,6 +36,7 @@ #include "net/dns/record_rdata.h" #include "net/socket/datagram_server_socket.h" #include "net/socket/udp_server_socket.h" +#include "services/network/public/cpp/features.h" // TODO(qingsi): Several features to implement: // @@ -966,10 +967,13 @@ void MdnsResponderManager::OnMdnsQueryReceived( // to handle only such records. Once we have expanded the API surface to // include the service publishing, the handling logic should be unified. const std::string qname = net::DNSDomainToString(query.qname()); - if (should_respond_to_generator_service_query_ && - qname == kMdnsNameGeneratorServiceInstanceName) { - HandleMdnsNameGeneratorServiceQuery(query, recv_socket_handler_id); - return; + if (base::FeatureList::IsEnabled( + features::kMdnsResponderGeneratedNameListing)) { + if (should_respond_to_generator_service_query_ && + qname == kMdnsNameGeneratorServiceInstanceName) { + HandleMdnsNameGeneratorServiceQuery(query, recv_socket_handler_id); + return; + } } for (auto& responder : responders_) diff --git a/chromium/services/network/mdns_responder_unittest.cc b/chromium/services/network/mdns_responder_unittest.cc index dd8d31f583d..9796b22d686 100644 --- a/chromium/services/network/mdns_responder_unittest.cc +++ b/chromium/services/network/mdns_responder_unittest.cc @@ -16,6 +16,7 @@ #include "base/memory/scoped_refptr.h" #include "base/strings/string_piece.h" #include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" #include "base/test/scoped_task_environment.h" #include "mojo/public/cpp/bindings/connector.h" #include "net/base/ip_address.h" @@ -25,6 +26,7 @@ #include "net/dns/dns_util.h" #include "net/dns/mock_mdns_socket_factory.h" #include "net/dns/public/dns_protocol.h" +#include "services/network/public/cpp/features.h" #include "services/network/public/mojom/mdns_responder.mojom.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -393,6 +395,8 @@ class MdnsResponderTest : public testing::Test { MdnsResponderTest() : failing_socket_factory_( scoped_task_environment_.GetMainThreadTaskRunner()) { + feature_list_.InitAndEnableFeature( + features::kMdnsResponderGeneratedNameListing); Reset(); } @@ -484,6 +488,7 @@ class MdnsResponderTest : public testing::Test { scoped_task_environment_.FastForwardBy(duration); } + base::test::ScopedFeatureList feature_list_; base::test::ScopedTaskEnvironment scoped_task_environment_{ base::test::ScopedTaskEnvironment::TimeSource::MOCK_TIME}; // Overrides the current thread task runner, so we can simulate the passage diff --git a/chromium/services/network/public/cpp/features.cc b/chromium/services/network/public/cpp/features.cc index d5a2195fe6e..01d581c5dda 100644 --- a/chromium/services/network/public/cpp/features.cc +++ b/chromium/services/network/public/cpp/features.cc @@ -99,6 +99,13 @@ const base::Feature kProactivelyThrottleLowPriorityRequests{ const base::Feature kCrossOriginEmbedderPolicy{ "CrossOriginEmbedderPolicy", base::FEATURE_DISABLED_BY_DEFAULT}; +// If this feature is enabled, the mDNS responder service responds to queries +// for TXT records associated with +// "Generated-Names._mdns_name_generator._udp.local" with a list of generated +// mDNS names (random UUIDs) in the TXT record data. +const base::Feature kMdnsResponderGeneratedNameListing{ + "MdnsResponderGeneratedNameListing", base::FEATURE_DISABLED_BY_DEFAULT}; + bool ShouldEnableOutOfBlinkCors() { // OOR-CORS requires NetworkService. if (!base::FeatureList::IsEnabled(features::kNetworkService)) diff --git a/chromium/services/network/public/cpp/features.h b/chromium/services/network/public/cpp/features.h index a6cba6481fa..ae832475603 100644 --- a/chromium/services/network/public/cpp/features.h +++ b/chromium/services/network/public/cpp/features.h @@ -39,6 +39,8 @@ COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kProactivelyThrottleLowPriorityRequests; COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kCrossOriginEmbedderPolicy; +COMPONENT_EXPORT(NETWORK_CPP) +extern const base::Feature kMdnsResponderGeneratedNameListing; COMPONENT_EXPORT(NETWORK_CPP) bool ShouldEnableOutOfBlinkCors(); diff --git a/chromium/services/video_capture/BUILD.gn b/chromium/services/video_capture/BUILD.gn index 3a0ba307d49..e8aa895f36b 100644 --- a/chromium/services/video_capture/BUILD.gn +++ b/chromium/services/video_capture/BUILD.gn @@ -2,14 +2,30 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//services/service_manager/public/cpp/service_executable.gni") import("//testing/test.gni") +service_executable("video_capture") { + sources = [ + "service_main.cc", + ] + + deps = [ + ":lib", + "//mojo/public/cpp/system", + "//services/video_capture/public/cpp", + "//services/video_capture/public/mojom", + ] +} + source_set("lib") { sources = [ "broadcasting_receiver.cc", "broadcasting_receiver.h", "device_factory_media_to_mojo_adapter.cc", "device_factory_media_to_mojo_adapter.h", + "device_factory_provider_impl.cc", + "device_factory_provider_impl.h", "device_media_to_mojo_adapter.cc", "device_media_to_mojo_adapter.h", "push_video_stream_subscription_impl.cc", @@ -18,14 +34,14 @@ source_set("lib") { "receiver_mojo_to_media_adapter.h", "scoped_access_permission_media_to_mojo_adapter.cc", "scoped_access_permission_media_to_mojo_adapter.h", + "service_impl.cc", + "service_impl.h", "shared_memory_virtual_device_mojo_adapter.cc", "shared_memory_virtual_device_mojo_adapter.h", "testing_controls_impl.cc", "testing_controls_impl.h", "texture_virtual_device_mojo_adapter.cc", "texture_virtual_device_mojo_adapter.h", - "video_capture_service_impl.cc", - "video_capture_service_impl.h", "video_source_impl.cc", "video_source_impl.h", "video_source_provider_impl.cc", @@ -43,6 +59,7 @@ source_set("lib") { "//media/capture/mojom:image_capture", "//media/mojo/common:common", "//mojo/public/cpp/system", + "//services/service_manager/public/cpp", "//services/video_capture/public/cpp", "//services/video_capture/public/mojom", "//services/video_capture/public/mojom:constants", @@ -61,6 +78,10 @@ source_set("tests") { sources = [ "broadcasting_receiver_unittest.cc", "device_media_to_mojo_adapter_unittest.cc", + "test/device_factory_provider_connectortest.cc", + "test/device_factory_provider_test.cc", + "test/device_factory_provider_test.h", + "test/device_factory_provider_unittest.cc", "test/fake_device_descriptor_test.cc", "test/fake_device_descriptor_test.h", "test/fake_device_descriptor_unittest.cc", @@ -73,22 +94,26 @@ source_set("tests") { "test/mock_device_unittest.cc", "test/mock_devices_changed_observer.cc", "test/mock_devices_changed_observer.h", - "test/service_lifecycle_unittest.cc", - "test/video_capture_service_test.cc", - "test/video_capture_service_test.h", - "test/video_capture_service_unittest.cc", "test/virtual_device_unittest.cc", "texture_virtual_device_mojo_adapter_unittest.cc", ] deps = [ ":lib", + ":video_capture", "//base/test:test_support", "//media/capture:test_support", "//media/capture/mojom:video_capture", + "//services/service_manager/public/cpp", + "//services/service_manager/public/cpp/test:test_support", + "//services/video_capture/public/cpp:manifest", "//services/video_capture/public/cpp:mocks", "//testing/gmock", "//testing/gtest", "//ui/gfx:test_support", ] + + data_deps = [ + ":video_capture", + ] } diff --git a/chromium/services/video_capture/device_factory.h b/chromium/services/video_capture/device_factory.h index 0898a1a21a8..4fe83766339 100644 --- a/chromium/services/video_capture/device_factory.h +++ b/chromium/services/video_capture/device_factory.h @@ -5,6 +5,7 @@ #ifndef SERVICES_VIDEO_CAPTURE_DEVICE_FACTORY_H_ #define SERVICES_VIDEO_CAPTURE_DEVICE_FACTORY_H_ +#include "services/service_manager/public/cpp/service_context_ref.h" #include "services/video_capture/public/mojom/device_factory.mojom.h" #if defined(OS_CHROMEOS) @@ -15,6 +16,8 @@ namespace video_capture { class DeviceFactory : public mojom::DeviceFactory { public: + virtual void SetServiceRef( + std::unique_ptr<service_manager::ServiceContextRef> service_ref) = 0; #if defined(OS_CHROMEOS) virtual void BindCrosImageCaptureRequest( cros::mojom::CrosImageCaptureRequest request) = 0; diff --git a/chromium/services/video_capture/device_factory_media_to_mojo_adapter.cc b/chromium/services/video_capture/device_factory_media_to_mojo_adapter.cc index 59416448e89..5b8e3f02d59 100644 --- a/chromium/services/video_capture/device_factory_media_to_mojo_adapter.cc +++ b/chromium/services/video_capture/device_factory_media_to_mojo_adapter.cc @@ -99,6 +99,11 @@ DeviceFactoryMediaToMojoAdapter::DeviceFactoryMediaToMojoAdapter( DeviceFactoryMediaToMojoAdapter::~DeviceFactoryMediaToMojoAdapter() = default; +void DeviceFactoryMediaToMojoAdapter::SetServiceRef( + std::unique_ptr<service_manager::ServiceContextRef> service_ref) { + service_ref_ = std::move(service_ref); +} + void DeviceFactoryMediaToMojoAdapter::GetDeviceInfos( GetDeviceInfosCallback callback) { capture_system_->GetDeviceInfosAsync( @@ -165,6 +170,7 @@ void DeviceFactoryMediaToMojoAdapter::CreateAndAddNewDevice( const std::string& device_id, mojom::DeviceRequest device_request, CreateDeviceCallback callback) { + DCHECK(service_ref_); std::unique_ptr<media::VideoCaptureDevice> media_device = capture_system_->CreateDevice(device_id); if (media_device == nullptr) { @@ -178,11 +184,11 @@ void DeviceFactoryMediaToMojoAdapter::CreateAndAddNewDevice( #if defined(OS_CHROMEOS) device_entry.device = std::make_unique<DeviceMediaToMojoAdapter>( - std::move(media_device), jpeg_decoder_factory_callback_, - jpeg_decoder_task_runner_); + service_ref_->Clone(), std::move(media_device), + jpeg_decoder_factory_callback_, jpeg_decoder_task_runner_); #else - device_entry.device = - std::make_unique<DeviceMediaToMojoAdapter>(std::move(media_device)); + device_entry.device = std::make_unique<DeviceMediaToMojoAdapter>( + service_ref_->Clone(), std::move(media_device)); #endif // defined(OS_CHROMEOS) device_entry.binding = std::make_unique<mojo::Binding<mojom::Device>>( device_entry.device.get(), std::move(device_request)); diff --git a/chromium/services/video_capture/device_factory_media_to_mojo_adapter.h b/chromium/services/video_capture/device_factory_media_to_mojo_adapter.h index 28095f25e7c..07db761c705 100644 --- a/chromium/services/video_capture/device_factory_media_to_mojo_adapter.h +++ b/chromium/services/video_capture/device_factory_media_to_mojo_adapter.h @@ -10,6 +10,7 @@ #include "media/capture/video/video_capture_device_client.h" #include "media/capture/video/video_capture_system.h" #include "mojo/public/cpp/bindings/binding.h" +#include "services/service_manager/public/cpp/service_context_ref.h" #include "services/video_capture/device_factory.h" #include "services/video_capture/public/mojom/devices_changed_observer.mojom.h" @@ -40,6 +41,8 @@ class DeviceFactoryMediaToMojoAdapter : public DeviceFactory { ~DeviceFactoryMediaToMojoAdapter() override; // DeviceFactory implementation. + void SetServiceRef( + std::unique_ptr<service_manager::ServiceContextRef> service_ref) override; void GetDeviceInfos(GetDeviceInfosCallback callback) override; void CreateDevice(const std::string& device_id, mojom::DeviceRequest device_request, @@ -80,6 +83,7 @@ class DeviceFactoryMediaToMojoAdapter : public DeviceFactory { CreateDeviceCallback callback); void OnClientConnectionErrorOrClose(const std::string& device_id); + std::unique_ptr<service_manager::ServiceContextRef> service_ref_; const std::unique_ptr<media::VideoCaptureSystem> capture_system_; std::map<std::string, ActiveDeviceEntry> active_devices_by_id_; diff --git a/chromium/services/video_capture/video_capture_service_impl.cc b/chromium/services/video_capture/device_factory_provider_impl.cc index b6a72388212..154a045edf3 100644 --- a/chromium/services/video_capture/video_capture_service_impl.cc +++ b/chromium/services/video_capture/device_factory_provider_impl.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "services/video_capture/video_capture_service_impl.h" +#include "services/video_capture/device_factory_provider_impl.h" #include <utility> @@ -15,9 +15,7 @@ #include "media/capture/video/video_capture_buffer_pool.h" #include "media/capture/video/video_capture_buffer_tracker.h" #include "media/capture/video/video_capture_system_impl.h" -#include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "services/video_capture/device_factory_media_to_mojo_adapter.h" -#include "services/video_capture/testing_controls_impl.h" #include "services/video_capture/video_source_provider_impl.h" #include "services/video_capture/virtual_device_enabled_device_factory.h" #include "services/viz/public/cpp/gpu/gpu.h" @@ -33,7 +31,7 @@ namespace video_capture { // GetTaskRunner() via WeakPtrs provided via GetWeakPtr(). To this end, // GetTaskRunner() and GetWeakPtr() can be called from any sequence, typically // the same as the one calling the constructor. -class VideoCaptureServiceImpl::GpuDependenciesContext { +class DeviceFactoryProviderImpl::GpuDependenciesContext { public: GpuDependenciesContext() : weak_factory_for_gpu_io_thread_(this) { gpu_io_task_runner_ = base::CreateSequencedTaskRunnerWithTraits( @@ -84,13 +82,19 @@ class VideoCaptureServiceImpl::GpuDependenciesContext { base::WeakPtrFactory<GpuDependenciesContext> weak_factory_for_gpu_io_thread_; }; -VideoCaptureServiceImpl::VideoCaptureServiceImpl( - mojo::PendingReceiver<mojom::VideoCaptureService> receiver, - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) - : receiver_(this, std::move(receiver)), - ui_task_runner_(std::move(ui_task_runner)) {} +DeviceFactoryProviderImpl::DeviceFactoryProviderImpl( + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, + base::OnceClosure request_service_quit_asap_cb) + : ui_task_runner_(std::move(ui_task_runner)), + request_service_quit_asap_cb_(std::move(request_service_quit_asap_cb)) { + // Unretained |this| is safe because |factory_bindings_| is owned by + // |this|. + factory_bindings_.set_connection_error_handler(base::BindRepeating( + &DeviceFactoryProviderImpl::OnFactoryOrSourceProviderClientDisconnected, + base::Unretained(this))); +} -VideoCaptureServiceImpl::~VideoCaptureServiceImpl() { +DeviceFactoryProviderImpl::~DeviceFactoryProviderImpl() { factory_bindings_.CloseAllBindings(); device_factory_.reset(); if (gpu_dependencies_context_) { @@ -99,8 +103,13 @@ VideoCaptureServiceImpl::~VideoCaptureServiceImpl() { } } +void DeviceFactoryProviderImpl::SetServiceRef( + std::unique_ptr<service_manager::ServiceContextRef> service_ref) { + service_ref_ = std::move(service_ref); +} + #if defined(OS_CHROMEOS) -void VideoCaptureServiceImpl::InjectGpuDependencies( +void DeviceFactoryProviderImpl::InjectGpuDependencies( mojom::AcceleratorFactoryPtr accelerator_factory) { LazyInitializeGpuDependenciesContext(); gpu_dependencies_context_->GetTaskRunner()->PostTask( @@ -108,46 +117,47 @@ void VideoCaptureServiceImpl::InjectGpuDependencies( gpu_dependencies_context_->GetWeakPtr(), accelerator_factory.PassInterface())); } - -void VideoCaptureServiceImpl::BindCrosImageCapture( - mojo::PendingReceiver<cros::mojom::CrosImageCapture> receiver) { - CHECK(device_factory_); - device_factory_->BindCrosImageCaptureRequest(std::move(receiver)); -} #endif // defined(OS_CHROMEOS) -void VideoCaptureServiceImpl::ConnectToDeviceFactory( +void DeviceFactoryProviderImpl::ConnectToDeviceFactory( mojom::DeviceFactoryRequest request) { + DCHECK(service_ref_); LazyInitializeDeviceFactory(); factory_bindings_.AddBinding(device_factory_.get(), std::move(request)); } -void VideoCaptureServiceImpl::ConnectToVideoSourceProvider( +void DeviceFactoryProviderImpl::ConnectToVideoSourceProvider( mojom::VideoSourceProviderRequest request) { LazyInitializeVideoSourceProvider(); video_source_provider_->AddClient(std::move(request)); } -void VideoCaptureServiceImpl::SetRetryCount(int32_t count) { +void DeviceFactoryProviderImpl::ShutdownServiceAsap() { + if (request_service_quit_asap_cb_) + std::move(request_service_quit_asap_cb_).Run(); +} + +void DeviceFactoryProviderImpl::SetRetryCount(int32_t count) { #if defined(OS_MACOSX) media::VideoCaptureDeviceFactoryMac::SetGetDeviceDescriptorsRetryCount(count); #endif } -void VideoCaptureServiceImpl::BindControlsForTesting( - mojo::PendingReceiver<mojom::TestingControls> receiver) { - mojo::MakeSelfOwnedReceiver(std::make_unique<TestingControlsImpl>(), - std::move(receiver)); -} - -void VideoCaptureServiceImpl::LazyInitializeGpuDependenciesContext() { +void DeviceFactoryProviderImpl::LazyInitializeGpuDependenciesContext() { if (!gpu_dependencies_context_) gpu_dependencies_context_ = std::make_unique<GpuDependenciesContext>(); } -void VideoCaptureServiceImpl::LazyInitializeDeviceFactory() { - if (device_factory_) +void DeviceFactoryProviderImpl::LazyInitializeDeviceFactory() { + DCHECK(service_ref_); + + // Factory may already exist but if no client was connected it will not have a + // ServiceRef. + if (device_factory_) { + if (factory_bindings_.empty()) + device_factory_->SetServiceRef(service_ref_->Clone()); return; + } LazyInitializeGpuDependenciesContext(); @@ -175,9 +185,11 @@ void VideoCaptureServiceImpl::LazyInitializeDeviceFactory() { std::make_unique<DeviceFactoryMediaToMojoAdapter>( std::move(video_capture_system))); #endif // defined(OS_CHROMEOS) + + device_factory_->SetServiceRef(service_ref_->Clone()); } -void VideoCaptureServiceImpl::LazyInitializeVideoSourceProvider() { +void DeviceFactoryProviderImpl::LazyInitializeVideoSourceProvider() { if (video_source_provider_) return; LazyInitializeDeviceFactory(); @@ -185,12 +197,39 @@ void VideoCaptureServiceImpl::LazyInitializeVideoSourceProvider() { device_factory_.get(), // Unretained(this) is safe, because |this| owns |video_source_provider_|. base::BindRepeating( - &VideoCaptureServiceImpl::OnLastSourceProviderClientDisconnected, + &DeviceFactoryProviderImpl::OnLastSourceProviderClientDisconnected, base::Unretained(this))); } -void VideoCaptureServiceImpl::OnLastSourceProviderClientDisconnected() { +void DeviceFactoryProviderImpl::OnLastSourceProviderClientDisconnected() { video_source_provider_.reset(); + OnFactoryOrSourceProviderClientDisconnected(); } +void DeviceFactoryProviderImpl::OnFactoryOrSourceProviderClientDisconnected() { + // If |video_source_provider_| still exists, it means there is still a client + // connected to it, in which case we also still need |device_factory_| to + // stay operational. + if (video_source_provider_) + return; + + // If neither |device_factory_| nor |video_source_provider_| have clients + // connected, release service ref so that service shutdown timeout can start + // if no other references are still alive. We keep the |device_factory_| + // instance alive in order to avoid losing state that would be expensive to + // reinitialize, e.g. having already enumerated the available devices. + if (factory_bindings_.empty()) { + device_factory_->SetServiceRef(nullptr); + } +} + +#if defined(OS_CHROMEOS) +void DeviceFactoryProviderImpl::BindCrosImageCaptureRequest( + cros::mojom::CrosImageCaptureRequest request) { + CHECK(device_factory_); + + device_factory_->BindCrosImageCaptureRequest(std::move(request)); +} +#endif // defined(OS_CHROMEOS) + } // namespace video_capture diff --git a/chromium/services/video_capture/video_capture_service_impl.h b/chromium/services/video_capture/device_factory_provider_impl.h index e98751cb170..950cd0b5464 100644 --- a/chromium/services/video_capture/video_capture_service_impl.h +++ b/chromium/services/video_capture/device_factory_provider_impl.h @@ -2,18 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SERVICES_VIDEO_CAPTURE_VIDEO_CAPTURE_SERVICE_IMPL_H_ -#define SERVICES_VIDEO_CAPTURE_VIDEO_CAPTURE_SERVICE_IMPL_H_ +#ifndef SERVICES_VIDEO_CAPTURE_DEVICE_FACTORY_PROVIDER_H_ +#define SERVICES_VIDEO_CAPTURE_DEVICE_FACTORY_PROVIDER_H_ #include <memory> #include "base/memory/scoped_refptr.h" #include "base/threading/thread.h" #include "mojo/public/cpp/bindings/binding_set.h" -#include "mojo/public/cpp/bindings/pending_receiver.h" -#include "mojo/public/cpp/bindings/receiver.h" +#include "services/service_manager/public/cpp/connector.h" +#include "services/service_manager/public/cpp/service.h" +#include "services/service_manager/public/cpp/service_context_ref.h" #include "services/video_capture/public/mojom/device_factory.mojom.h" -#include "services/video_capture/public/mojom/video_capture_service.mojom.h" +#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" #if defined(OS_CHROMEOS) #include "media/capture/video/chromeos/mojo/cros_image_capture.mojom.h" @@ -24,26 +25,31 @@ namespace video_capture { class VirtualDeviceEnabledDeviceFactory; class VideoSourceProviderImpl; -class VideoCaptureServiceImpl : public mojom::VideoCaptureService { +class DeviceFactoryProviderImpl : public mojom::DeviceFactoryProvider { public: - explicit VideoCaptureServiceImpl( - mojo::PendingReceiver<mojom::VideoCaptureService> receiver, - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner); - ~VideoCaptureServiceImpl() override; + explicit DeviceFactoryProviderImpl( + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, + base::OnceClosure request_service_quit_asap_cb); + ~DeviceFactoryProviderImpl() override; - // mojom::VideoCaptureService implementation. + void SetServiceRef( + std::unique_ptr<service_manager::ServiceContextRef> service_ref); + + // mojom::DeviceFactoryProvider implementation. #if defined(OS_CHROMEOS) void InjectGpuDependencies( mojom::AcceleratorFactoryPtr accelerator_factory) override; - void BindCrosImageCapture( - mojo::PendingReceiver<cros::mojom::CrosImageCapture> receiver) override; #endif // defined(OS_CHROMEOS) void ConnectToDeviceFactory(mojom::DeviceFactoryRequest request) override; void ConnectToVideoSourceProvider( mojom::VideoSourceProviderRequest request) override; + void ShutdownServiceAsap() override; void SetRetryCount(int32_t count) override; - void BindControlsForTesting( - mojo::PendingReceiver<mojom::TestingControls> receiver) override; + +#if defined(OS_CHROMEOS) + void BindCrosImageCaptureRequest( + cros::mojom::CrosImageCaptureRequest request); +#endif // defined(OS_CHROMEOS) private: class GpuDependenciesContext; @@ -52,18 +58,20 @@ class VideoCaptureServiceImpl : public mojom::VideoCaptureService { void LazyInitializeDeviceFactory(); void LazyInitializeVideoSourceProvider(); void OnLastSourceProviderClientDisconnected(); + void OnFactoryOrSourceProviderClientDisconnected(); - mojo::Receiver<mojom::VideoCaptureService> receiver_; mojo::BindingSet<mojom::DeviceFactory> factory_bindings_; std::unique_ptr<VirtualDeviceEnabledDeviceFactory> device_factory_; std::unique_ptr<VideoSourceProviderImpl> video_source_provider_; + std::unique_ptr<service_manager::ServiceContextRef> service_ref_; std::unique_ptr<GpuDependenciesContext> gpu_dependencies_context_; scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; + base::OnceClosure request_service_quit_asap_cb_; - DISALLOW_COPY_AND_ASSIGN(VideoCaptureServiceImpl); + DISALLOW_COPY_AND_ASSIGN(DeviceFactoryProviderImpl); }; } // namespace video_capture -#endif // SERVICES_VIDEO_CAPTURE_VIDEO_CAPTURE_SERVICE_IMPL_H_ +#endif // SERVICES_VIDEO_CAPTURE_DEVICE_FACTORY_PROVIDER_H_ diff --git a/chromium/services/video_capture/device_media_to_mojo_adapter.cc b/chromium/services/video_capture/device_media_to_mojo_adapter.cc index 1ce673c0e8d..dfb0abd8f2a 100644 --- a/chromium/services/video_capture/device_media_to_mojo_adapter.cc +++ b/chromium/services/video_capture/device_media_to_mojo_adapter.cc @@ -40,18 +40,24 @@ namespace video_capture { #if defined(OS_CHROMEOS) DeviceMediaToMojoAdapter::DeviceMediaToMojoAdapter( + std::unique_ptr<service_manager::ServiceContextRef> service_ref, std::unique_ptr<media::VideoCaptureDevice> device, media::MojoMjpegDecodeAcceleratorFactoryCB jpeg_decoder_factory_callback, scoped_refptr<base::SequencedTaskRunner> jpeg_decoder_task_runner) - : device_(std::move(device)), + : service_ref_(std::move(service_ref)), + device_(std::move(device)), jpeg_decoder_factory_callback_(std::move(jpeg_decoder_factory_callback)), jpeg_decoder_task_runner_(std::move(jpeg_decoder_task_runner)), device_started_(false), weak_factory_(this) {} #else DeviceMediaToMojoAdapter::DeviceMediaToMojoAdapter( + std::unique_ptr<service_manager::ServiceContextRef> service_ref, std::unique_ptr<media::VideoCaptureDevice> device) - : device_(std::move(device)), device_started_(false), weak_factory_(this) {} + : service_ref_(std::move(service_ref)), + device_(std::move(device)), + device_started_(false), + weak_factory_(this) {} #endif // defined(OS_CHROMEOS) DeviceMediaToMojoAdapter::~DeviceMediaToMojoAdapter() { diff --git a/chromium/services/video_capture/device_media_to_mojo_adapter.h b/chromium/services/video_capture/device_media_to_mojo_adapter.h index 2e9d1ec2a23..4ebf2e5246c 100644 --- a/chromium/services/video_capture/device_media_to_mojo_adapter.h +++ b/chromium/services/video_capture/device_media_to_mojo_adapter.h @@ -10,6 +10,7 @@ #include "media/capture/video/video_capture_device_client.h" #include "media/capture/video/video_capture_device_factory.h" #include "media/capture/video_capture_types.h" +#include "services/service_manager/public/cpp/service_context_ref.h" #include "services/video_capture/public/mojom/device.mojom.h" #if defined(OS_CHROMEOS) @@ -27,11 +28,13 @@ class DeviceMediaToMojoAdapter : public mojom::Device { public: #if defined(OS_CHROMEOS) DeviceMediaToMojoAdapter( + std::unique_ptr<service_manager::ServiceContextRef> service_ref, std::unique_ptr<media::VideoCaptureDevice> device, media::MojoMjpegDecodeAcceleratorFactoryCB jpeg_decoder_factory_callback, scoped_refptr<base::SequencedTaskRunner> jpeg_decoder_task_runner); #else DeviceMediaToMojoAdapter( + std::unique_ptr<service_manager::ServiceContextRef> service_ref, std::unique_ptr<media::VideoCaptureDevice> device); #endif // defined(OS_CHROMEOS) ~DeviceMediaToMojoAdapter() override; @@ -54,6 +57,7 @@ class DeviceMediaToMojoAdapter : public mojom::Device { static int max_buffer_pool_buffer_count(); private: + const std::unique_ptr<service_manager::ServiceContextRef> service_ref_; const std::unique_ptr<media::VideoCaptureDevice> device_; #if defined(OS_CHROMEOS) const media::MojoMjpegDecodeAcceleratorFactoryCB diff --git a/chromium/services/video_capture/device_media_to_mojo_adapter_unittest.cc b/chromium/services/video_capture/device_media_to_mojo_adapter_unittest.cc index 7ae494ebb71..818df33c4cb 100644 --- a/chromium/services/video_capture/device_media_to_mojo_adapter_unittest.cc +++ b/chromium/services/video_capture/device_media_to_mojo_adapter_unittest.cc @@ -29,10 +29,12 @@ class DeviceMediaToMojoAdapterTest : public ::testing::Test { mock_device_ptr_ = mock_device.get(); #if defined(OS_CHROMEOS) adapter_ = std::make_unique<DeviceMediaToMojoAdapter>( + std::unique_ptr<service_manager::ServiceContextRef>(), std::move(mock_device), base::DoNothing(), base::ThreadTaskRunnerHandle::Get()); #else adapter_ = std::make_unique<DeviceMediaToMojoAdapter>( + std::unique_ptr<service_manager::ServiceContextRef>(), std::move(mock_device)); #endif // defined(OS_CHROMEOS) } diff --git a/chromium/services/video_capture/public/cpp/BUILD.gn b/chromium/services/video_capture/public/cpp/BUILD.gn index 98462de10fd..07fdfdaa744 100644 --- a/chromium/services/video_capture/public/cpp/BUILD.gn +++ b/chromium/services/video_capture/public/cpp/BUILD.gn @@ -14,6 +14,7 @@ source_set("cpp") { "//base", "//media", "//media/capture:capture", + "//services/service_manager/public/cpp", "//services/video_capture/public/mojom", ] @@ -22,20 +23,36 @@ source_set("cpp") { ] } +source_set("manifest") { + sources = [ + "manifest.cc", + "manifest.h", + ] + deps = [ + "//base", + "//services/service_manager/public/cpp", + "//services/video_capture/public/mojom", + "//services/video_capture/public/mojom:constants", + ] + if (is_chromeos) { + deps += [ "//media/capture/video/chromeos/mojo:cros_camera" ] + } +} + source_set("mocks") { testonly = true sources = [ "mock_device_factory.cc", "mock_device_factory.h", + "mock_device_factory_provider.cc", + "mock_device_factory_provider.h", "mock_producer.cc", "mock_producer.h", "mock_push_subscription.cc", "mock_push_subscription.h", "mock_receiver.cc", "mock_receiver.h", - "mock_video_capture_service.cc", - "mock_video_capture_service.h", "mock_video_source.cc", "mock_video_source.h", "mock_video_source_provider.cc", diff --git a/chromium/services/video_capture/public/cpp/manifest.cc b/chromium/services/video_capture/public/cpp/manifest.cc new file mode 100644 index 00000000000..fc1b20d86a1 --- /dev/null +++ b/chromium/services/video_capture/public/cpp/manifest.cc @@ -0,0 +1,43 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/video_capture/public/cpp/manifest.h" + +#if defined(OS_CHROMEOS) +#include "media/capture/video/chromeos/mojo/cros_image_capture.mojom.h" +#endif // defined(OS_CHROMEOS) +#include "services/service_manager/public/cpp/manifest_builder.h" +#include "services/video_capture/public/mojom/constants.mojom.h" +#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" +#include "services/video_capture/public/mojom/testing_controls.mojom.h" + +namespace video_capture { + +service_manager::Manifest GetManifest( + service_manager::Manifest::ExecutionMode execution_mode) { + return service_manager::Manifest { + service_manager::ManifestBuilder() + .WithServiceName(mojom::kServiceName) + .WithDisplayName("Video Capture") + .WithOptions(service_manager::ManifestOptionsBuilder() + .WithExecutionMode(execution_mode) + .WithSandboxType("none") + .WithInstanceSharingPolicy( + service_manager::Manifest::InstanceSharingPolicy:: + kSharedAcrossGroups) + .Build()) + .ExposeCapability("capture", service_manager::Manifest::InterfaceList< +#if defined(OS_CHROMEOS) + cros::mojom::CrosImageCapture, +#endif // defined(OS_CHROMEOS) + mojom::DeviceFactoryProvider>()) + .ExposeCapability( + "tests", + service_manager::Manifest::InterfaceList< + mojom::DeviceFactoryProvider, mojom::TestingControls>()) + .Build() + }; +} + +} // namespace video_capture diff --git a/chromium/services/video_capture/public/cpp/manifest.h b/chromium/services/video_capture/public/cpp/manifest.h new file mode 100644 index 00000000000..d28ef4a8078 --- /dev/null +++ b/chromium/services/video_capture/public/cpp/manifest.h @@ -0,0 +1,17 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MANIFEST_H_ +#define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MANIFEST_H_ + +#include "services/service_manager/public/cpp/manifest.h" + +namespace video_capture { + +service_manager::Manifest GetManifest( + service_manager::Manifest::ExecutionMode execution_mode); + +} // namespace video_capture + +#endif // SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MANIFEST_H_ diff --git a/chromium/services/video_capture/public/cpp/mock_video_capture_service.cc b/chromium/services/video_capture/public/cpp/mock_device_factory_provider.cc index f2dbb05b3d2..2dbc71c2d18 100644 --- a/chromium/services/video_capture/public/cpp/mock_video_capture_service.cc +++ b/chromium/services/video_capture/public/cpp/mock_device_factory_provider.cc @@ -2,26 +2,26 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "services/video_capture/public/cpp/mock_video_capture_service.h" +#include "services/video_capture/public/cpp/mock_device_factory_provider.h" namespace video_capture { -MockVideoCaptureService::MockVideoCaptureService() {} +MockDeviceFactoryProvider::MockDeviceFactoryProvider() {} -MockVideoCaptureService::~MockVideoCaptureService() = default; +MockDeviceFactoryProvider::~MockDeviceFactoryProvider() = default; -void MockVideoCaptureService::ConnectToDeviceFactory( +void MockDeviceFactoryProvider::ConnectToDeviceFactory( video_capture::mojom::DeviceFactoryRequest request) { DoConnectToDeviceFactory(request); } -void MockVideoCaptureService::ConnectToVideoSourceProvider( +void MockDeviceFactoryProvider::ConnectToVideoSourceProvider( video_capture::mojom::VideoSourceProviderRequest request) { DoConnectToVideoSourceProvider(request); } #if defined(OS_CHROMEOS) -void MockVideoCaptureService::InjectGpuDependencies( +void MockDeviceFactoryProvider::InjectGpuDependencies( video_capture::mojom::AcceleratorFactoryPtr accelerator_factory) { DoInjectGpuDependencies(accelerator_factory); } diff --git a/chromium/services/video_capture/public/cpp/mock_video_capture_service.h b/chromium/services/video_capture/public/cpp/mock_device_factory_provider.h index da63ced2b59..645a75d9353 100644 --- a/chromium/services/video_capture/public/cpp/mock_video_capture_service.h +++ b/chromium/services/video_capture/public/cpp/mock_device_factory_provider.h @@ -2,19 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_VIDEO_CAPTURE_SERVICE_H_ -#define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_VIDEO_CAPTURE_SERVICE_H_ +#ifndef SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_DEVICE_FACTORY_PROVIDER_H_ +#define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_DEVICE_FACTORY_PROVIDER_H_ -#include "services/video_capture/public/mojom/video_capture_service.mojom.h" +#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" #include "testing/gmock/include/gmock/gmock.h" namespace video_capture { -class MockVideoCaptureService - : public video_capture::mojom::VideoCaptureService { +class MockDeviceFactoryProvider + : public video_capture::mojom::DeviceFactoryProvider { public: - MockVideoCaptureService(); - ~MockVideoCaptureService() override; + MockDeviceFactoryProvider(); + ~MockDeviceFactoryProvider() override; void ConnectToDeviceFactory( video_capture::mojom::DeviceFactoryRequest request) override; @@ -29,22 +29,17 @@ class MockVideoCaptureService MOCK_METHOD1( DoInjectGpuDependencies, void(video_capture::mojom::AcceleratorFactoryPtr& accelerator_factory)); - - void BindCrosImageCapture( - mojo::PendingReceiver<cros::mojom::CrosImageCapture>) override {} #endif // defined(OS_CHROMEOS - void BindControlsForTesting( - mojo::PendingReceiver<mojom::TestingControls>) override {} - MOCK_METHOD1(SetShutdownDelayInSeconds, void(float seconds)); MOCK_METHOD1(DoConnectToDeviceFactory, void(video_capture::mojom::DeviceFactoryRequest& request)); MOCK_METHOD1(DoConnectToVideoSourceProvider, void(video_capture::mojom::VideoSourceProviderRequest& request)); + MOCK_METHOD0(ShutdownServiceAsap, void()); MOCK_METHOD1(SetRetryCount, void(int32_t)); }; } // namespace video_capture -#endif // SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_VIDEO_CAPTURE_SERVICE_H_ +#endif // SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_DEVICE_FACTORY_PROVIDER_H_ diff --git a/chromium/services/video_capture/public/mojom/BUILD.gn b/chromium/services/video_capture/public/mojom/BUILD.gn index 11b2e39afdd..908532aeb6f 100644 --- a/chromium/services/video_capture/public/mojom/BUILD.gn +++ b/chromium/services/video_capture/public/mojom/BUILD.gn @@ -8,12 +8,12 @@ mojom("mojom") { sources = [ "device.mojom", "device_factory.mojom", + "device_factory_provider.mojom", "devices_changed_observer.mojom", "producer.mojom", "receiver.mojom", "scoped_access_permission.mojom", "testing_controls.mojom", - "video_capture_service.mojom", "video_source.mojom", "video_source_provider.mojom", "virtual_device.mojom", @@ -27,10 +27,7 @@ mojom("mojom") { ] if (is_chromeos) { - deps += [ - "//components/chromeos_camera/common", - "//media/capture/video/chromeos/mojo:cros_camera", - ] + deps += [ "//components/chromeos_camera/common" ] } } diff --git a/chromium/services/video_capture/public/mojom/constants.mojom b/chromium/services/video_capture/public/mojom/constants.mojom index d13930e0a76..ee5cd3e3a05 100644 --- a/chromium/services/video_capture/public/mojom/constants.mojom +++ b/chromium/services/video_capture/public/mojom/constants.mojom @@ -4,4 +4,5 @@ module video_capture.mojom; +const string kServiceName = "video_capture"; const int32 kInvalidBufferId = -1; diff --git a/chromium/services/video_capture/public/mojom/video_capture_service.mojom b/chromium/services/video_capture/public/mojom/device_factory_provider.mojom index 916e8efca19..4ec2285ddb0 100644 --- a/chromium/services/video_capture/public/mojom/video_capture_service.mojom +++ b/chromium/services/video_capture/public/mojom/device_factory_provider.mojom @@ -7,13 +7,9 @@ module video_capture.mojom; [EnableIf=is_chromeos] import "components/chromeos_camera/common/mjpeg_decode_accelerator.mojom"; import "services/video_capture/public/mojom/device_factory.mojom"; -import "services/video_capture/public/mojom/testing_controls.mojom"; import "services/video_capture/public/mojom/video_source_provider.mojom"; [EnableIf=is_chromeos] -import "media/capture/video/chromeos/mojo/cros_image_capture.mojom"; - -[EnableIf=is_chromeos] interface AcceleratorFactory { // Creates a new JpegDecodeAccelerator and binds it to |jda|. CreateJpegDecodeAccelerator( @@ -29,24 +25,22 @@ interface AcceleratorFactory { // to be called once before any call to ConnectToDeviceFactory() is made. // Calling InjectGpuDependencies() is optional. If it is not called, MJPEG // decoding will be performed without gpu acceleration. -interface VideoCaptureService { +// TODO(chfremer): Consider renaming this to VideoCaptureService. +interface DeviceFactoryProvider { [EnableIf=is_chromeos] InjectGpuDependencies(AcceleratorFactory accelerator_factory); - // Binds a Chrome OS camera capture interface. - [EnableIf=is_chromeos] - BindCrosImageCapture(pending_receiver<cros.mojom.CrosImageCapture> receiver); - // Legacy API. Supports one client per device. ConnectToDeviceFactory(DeviceFactory& request); // Current API. Supports multiple clients per source. ConnectToVideoSourceProvider(VideoSourceProvider& request); + // Lets the service to close all connections and ask the service_manager to be + // shut down. + ShutdownServiceAsap(); + // Sets a retry count that is used by the service for logging UMA events in // the context of investigation for https://crbug.com/643068. SetRetryCount(int32 count); - - // Binds a test-only interface for use by unit tests. - BindControlsForTesting(pending_receiver<TestingControls> receiver); }; diff --git a/chromium/services/video_capture/service_impl.cc b/chromium/services/video_capture/service_impl.cc new file mode 100644 index 00000000000..5bf14321766 --- /dev/null +++ b/chromium/services/video_capture/service_impl.cc @@ -0,0 +1,192 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/video_capture/service_impl.h" + +#include <utility> + +#include "base/bind.h" +#include "build/build_config.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "services/video_capture/device_factory_provider_impl.h" +#include "services/video_capture/public/mojom/constants.mojom.h" +#include "services/video_capture/public/uma/video_capture_service_event.h" +#include "services/video_capture/testing_controls_impl.h" + +#if defined(OS_CHROMEOS) +#include "media/capture/video/chromeos/mojo/cros_image_capture.mojom.h" +#endif // defined(OS_CHROMEOS) + +namespace video_capture { + +namespace { + +#if defined(OS_ANDROID) +// On Android, we do not use automatic service shutdown, because when shutting +// down the service, we lose caching of the supported formats, and re-querying +// these can take several seconds on certain Android devices. +constexpr base::Optional<base::TimeDelta> kDefaultIdleTimeout; +#else +constexpr base::Optional<base::TimeDelta> kDefaultIdleTimeout = + base::TimeDelta::FromSeconds(5); +#endif + +} // namespace + +ServiceImpl::ServiceImpl( + service_manager::mojom::ServiceRequest request, + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) + : ServiceImpl(std::move(request), + std::move(ui_task_runner), + kDefaultIdleTimeout) {} + +ServiceImpl::ServiceImpl( + service_manager::mojom::ServiceRequest request, + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, + base::Optional<base::TimeDelta> idle_timeout) + : binding_(this, std::move(request)), + keepalive_(&binding_, idle_timeout), + ui_task_runner_(std::move(ui_task_runner)) { + keepalive_.AddObserver(this); +} + +ServiceImpl::~ServiceImpl() { + DCHECK(thread_checker_.CalledOnValidThread()); + keepalive_.RemoveObserver(this); +} + +void ServiceImpl::SetFactoryProviderClientConnectedObserver( + base::RepeatingClosure observer_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + factory_provider_client_connected_cb_ = std::move(observer_cb); +} + +void ServiceImpl::SetFactoryProviderClientDisconnectedObserver( + base::RepeatingClosure observer_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + factory_provider_client_disconnected_cb_ = std::move(observer_cb); +} + +void ServiceImpl::SetShutdownTimeoutCancelledObserver( + base::RepeatingClosure observer_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + shutdown_timeout_cancelled_cb_ = std::move(observer_cb); +} + +bool ServiceImpl::HasNoContextRefs() { + DCHECK(thread_checker_.CalledOnValidThread()); + return keepalive_.HasNoRefs(); +} + +void ServiceImpl::ShutdownServiceAsap() { + binding_.RequestClose(); +} + +void ServiceImpl::OnStart() { + DCHECK(thread_checker_.CalledOnValidThread()); + + video_capture::uma::LogVideoCaptureServiceEvent( + video_capture::uma::SERVICE_STARTED); + + registry_.AddInterface<mojom::DeviceFactoryProvider>( + // Unretained |this| is safe because |registry_| is owned by |this|. + base::BindRepeating(&ServiceImpl::OnDeviceFactoryProviderRequest, + base::Unretained(this))); + registry_.AddInterface<mojom::TestingControls>( + // Unretained |this| is safe because |registry_| is owned by |this|. + base::BindRepeating(&ServiceImpl::OnTestingControlsRequest, + base::Unretained(this))); + +#if defined(OS_CHROMEOS) + registry_.AddInterface<cros::mojom::CrosImageCapture>(base::BindRepeating( + &ServiceImpl::OnCrosImageCaptureRequest, base::Unretained(this))); +#endif // defined(OS_CHROMEOS) + + // Unretained |this| is safe because |factory_provider_bindings_| is owned by + // |this|. + factory_provider_bindings_.set_connection_error_handler(base::BindRepeating( + &ServiceImpl::OnProviderClientDisconnected, base::Unretained(this))); +} + +void ServiceImpl::OnBindInterface( + const service_manager::BindSourceInfo& source_info, + const std::string& interface_name, + mojo::ScopedMessagePipeHandle interface_pipe) { + DCHECK(thread_checker_.CalledOnValidThread()); + registry_.BindInterface(interface_name, std::move(interface_pipe)); +} + +bool ServiceImpl::OnServiceManagerConnectionLost() { + DCHECK(thread_checker_.CalledOnValidThread()); + return true; +} + +void ServiceImpl::OnIdleTimeout() { + video_capture::uma::LogVideoCaptureServiceEvent( + video_capture::uma::SERVICE_SHUTTING_DOWN_BECAUSE_NO_CLIENT); +} + +void ServiceImpl::OnIdleTimeoutCancelled() { + video_capture::uma::LogVideoCaptureServiceEvent( + video_capture::uma::SERVICE_SHUTDOWN_TIMEOUT_CANCELED); + if (shutdown_timeout_cancelled_cb_) + shutdown_timeout_cancelled_cb_.Run(); +} + +void ServiceImpl::OnDeviceFactoryProviderRequest( + mojom::DeviceFactoryProviderRequest request) { + DCHECK(thread_checker_.CalledOnValidThread()); + LazyInitializeDeviceFactoryProvider(); + if (factory_provider_bindings_.empty()) + device_factory_provider_->SetServiceRef(keepalive_.CreateRef()); + factory_provider_bindings_.AddBinding(device_factory_provider_.get(), + std::move(request)); + + if (!factory_provider_client_connected_cb_.is_null()) { + factory_provider_client_connected_cb_.Run(); + } +} + +void ServiceImpl::OnTestingControlsRequest( + mojom::TestingControlsRequest request) { + DCHECK(thread_checker_.CalledOnValidThread()); + mojo::MakeStrongBinding( + std::make_unique<TestingControlsImpl>(keepalive_.CreateRef()), + std::move(request)); +} + +#if defined(OS_CHROMEOS) +void ServiceImpl::OnCrosImageCaptureRequest( + cros::mojom::CrosImageCaptureRequest request) { + LazyInitializeDeviceFactoryProvider(); + device_factory_provider_->BindCrosImageCaptureRequest(std::move(request)); +} +#endif // defined(OS_CHROMEOS) + +void ServiceImpl::LazyInitializeDeviceFactoryProvider() { + if (device_factory_provider_) + return; + + // Use of base::Unretained() is safe because |this| owns, and therefore + // outlives |device_factory_provider_| + device_factory_provider_ = std::make_unique<DeviceFactoryProviderImpl>( + ui_task_runner_, base::BindOnce(&ServiceImpl::ShutdownServiceAsap, + base::Unretained(this))); +} + +void ServiceImpl::OnProviderClientDisconnected() { + // If last client has disconnected, release service ref so that service + // shutdown timeout starts if no other references are still alive. + // We keep the |device_factory_provider_| instance alive in order to avoid + // losing state that would be expensive to reinitialize, e.g. having + // already enumerated the available devices. + if (factory_provider_bindings_.empty()) + device_factory_provider_->SetServiceRef(nullptr); + + if (!factory_provider_client_disconnected_cb_.is_null()) { + factory_provider_client_disconnected_cb_.Run(); + } +} + +} // namespace video_capture diff --git a/chromium/services/video_capture/service_impl.h b/chromium/services/video_capture/service_impl.h new file mode 100644 index 00000000000..65b2a12aba3 --- /dev/null +++ b/chromium/services/video_capture/service_impl.h @@ -0,0 +1,107 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_VIDEO_CAPTURE_SERVICE_IMPL_H_ +#define SERVICES_VIDEO_CAPTURE_SERVICE_IMPL_H_ + +#include <memory> + +#include "base/memory/scoped_refptr.h" +#include "base/optional.h" +#include "base/threading/thread_checker.h" +#include "base/time/time.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "services/service_manager/public/cpp/binder_registry.h" +#include "services/service_manager/public/cpp/service.h" +#include "services/service_manager/public/cpp/service_binding.h" +#include "services/service_manager/public/cpp/service_keepalive.h" +#include "services/video_capture/device_factory_provider_impl.h" +#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" +#include "services/video_capture/public/mojom/testing_controls.mojom.h" + +#if defined(OS_WIN) +#include "base/win/scoped_com_initializer.h" +#endif + +#if defined(OS_CHROMEOS) +#include "media/capture/video/chromeos/mojo/cros_image_capture.mojom.h" +#endif // defined(OS_CHROMEOS) + +namespace base { +class SingleThreadTaskRunner; +} + +namespace video_capture { + +class ServiceImpl : public service_manager::Service, + public service_manager::ServiceKeepalive::Observer { + public: + ServiceImpl(service_manager::mojom::ServiceRequest request, + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner); + + // Constructs a service instance which overrides the default idle timeout + // behavior. + ServiceImpl(service_manager::mojom::ServiceRequest request, + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, + base::Optional<base::TimeDelta> idle_timeout); + + ~ServiceImpl() override; + + void SetFactoryProviderClientConnectedObserver( + base::RepeatingClosure observer_cb); + void SetFactoryProviderClientDisconnectedObserver( + base::RepeatingClosure observer_cb); + void SetShutdownTimeoutCancelledObserver(base::RepeatingClosure observer_cb); + bool HasNoContextRefs(); + + void ShutdownServiceAsap(); + + // service_manager::Service implementation. + void OnStart() override; + void OnBindInterface(const service_manager::BindSourceInfo& source_info, + const std::string& interface_name, + mojo::ScopedMessagePipeHandle interface_pipe) override; + bool OnServiceManagerConnectionLost() override; + + // service_manager::ServiceKeepalive::Observer implementation. + void OnIdleTimeout() override; + void OnIdleTimeoutCancelled() override; + + private: + void OnDeviceFactoryProviderRequest( + mojom::DeviceFactoryProviderRequest request); + void OnTestingControlsRequest(mojom::TestingControlsRequest request); +#if defined(OS_CHROMEOS) + void OnCrosImageCaptureRequest(cros::mojom::CrosImageCaptureRequest request); +#endif // defined(OS_CHROMEOS) + void MaybeRequestQuitDelayed(); + void MaybeRequestQuit(); + void LazyInitializeDeviceFactoryProvider(); + void OnProviderClientDisconnected(); + + service_manager::ServiceBinding binding_; + service_manager::ServiceKeepalive keepalive_; + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; + +#if defined(OS_WIN) + // COM must be initialized in order to access the video capture devices. + base::win::ScopedCOMInitializer com_initializer_; +#endif + service_manager::BinderRegistry registry_; + mojo::BindingSet<mojom::DeviceFactoryProvider> factory_provider_bindings_; + std::unique_ptr<DeviceFactoryProviderImpl> device_factory_provider_; + + // Callbacks that can optionally be set by clients. + base::RepeatingClosure factory_provider_client_connected_cb_; + base::RepeatingClosure factory_provider_client_disconnected_cb_; + base::RepeatingClosure shutdown_timeout_cancelled_cb_; + + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(ServiceImpl); +}; + +} // namespace video_capture + +#endif // SERVICES_VIDEO_CAPTURE_SERVICE_IMPL_H_ diff --git a/chromium/services/video_capture/service_main.cc b/chromium/services/video_capture/service_main.cc new file mode 100644 index 00000000000..77f46359fd9 --- /dev/null +++ b/chromium/services/video_capture/service_main.cc @@ -0,0 +1,14 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/service_manager/public/cpp/service_executable/service_main.h" +#include "base/task/single_thread_task_executor.h" +#include "services/video_capture/service_impl.h" + +void ServiceMain(service_manager::mojom::ServiceRequest request) { + base::SingleThreadTaskExecutor main_thread_task_executor; + video_capture::ServiceImpl(std::move(request), + main_thread_task_executor.task_runner()) + .RunUntilTermination(); +} diff --git a/chromium/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc b/chromium/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc index 265ed010d9b..2cecb0fef8c 100644 --- a/chromium/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc +++ b/chromium/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc @@ -29,9 +29,11 @@ void OnNewBufferAcknowleged( namespace video_capture { SharedMemoryVirtualDeviceMojoAdapter::SharedMemoryVirtualDeviceMojoAdapter( + std::unique_ptr<service_manager::ServiceContextRef> service_ref, mojom::ProducerPtr producer, bool send_buffer_handles_to_producer_as_raw_file_descriptors) - : producer_(std::move(producer)), + : service_ref_(std::move(service_ref)), + producer_(std::move(producer)), send_buffer_handles_to_producer_as_raw_file_descriptors_( send_buffer_handles_to_producer_as_raw_file_descriptors), buffer_pool_(new media::VideoCaptureBufferPoolImpl( diff --git a/chromium/services/video_capture/shared_memory_virtual_device_mojo_adapter.h b/chromium/services/video_capture/shared_memory_virtual_device_mojo_adapter.h index 4f35010d9bb..45c4a2d44d1 100644 --- a/chromium/services/video_capture/shared_memory_virtual_device_mojo_adapter.h +++ b/chromium/services/video_capture/shared_memory_virtual_device_mojo_adapter.h @@ -8,6 +8,7 @@ #include "base/sequence_checker.h" #include "media/capture/video/video_capture_buffer_pool.h" #include "media/capture/video_capture_types.h" +#include "services/service_manager/public/cpp/service_context_ref.h" #include "services/video_capture/public/mojom/device.mojom.h" #include "services/video_capture/public/mojom/producer.mojom.h" #include "services/video_capture/public/mojom/receiver.mojom.h" @@ -20,6 +21,7 @@ class SharedMemoryVirtualDeviceMojoAdapter public mojom::Device { public: SharedMemoryVirtualDeviceMojoAdapter( + std::unique_ptr<service_manager::ServiceContextRef> service_ref, mojom::ProducerPtr producer, bool send_buffer_handles_to_producer_as_raw_file_descriptors = false); ~SharedMemoryVirtualDeviceMojoAdapter() override; @@ -52,6 +54,7 @@ class SharedMemoryVirtualDeviceMojoAdapter private: void OnReceiverConnectionErrorOrClose(); + const std::unique_ptr<service_manager::ServiceContextRef> service_ref_; mojom::ReceiverPtr receiver_; mojom::ProducerPtr producer_; const bool send_buffer_handles_to_producer_as_raw_file_descriptors_; diff --git a/chromium/services/video_capture/testing_controls_impl.cc b/chromium/services/video_capture/testing_controls_impl.cc index 3dc4e69af3a..e89eb86ec3a 100644 --- a/chromium/services/video_capture/testing_controls_impl.cc +++ b/chromium/services/video_capture/testing_controls_impl.cc @@ -4,11 +4,11 @@ #include "services/video_capture/testing_controls_impl.h" -#include "base/logging.h" - namespace video_capture { -TestingControlsImpl::TestingControlsImpl() = default; +TestingControlsImpl::TestingControlsImpl( + std::unique_ptr<service_manager::ServiceContextRef> service_ref) + : service_ref_(std::move(service_ref)) {} TestingControlsImpl::~TestingControlsImpl() = default; diff --git a/chromium/services/video_capture/testing_controls_impl.h b/chromium/services/video_capture/testing_controls_impl.h index eb051d14b6e..860820be50d 100644 --- a/chromium/services/video_capture/testing_controls_impl.h +++ b/chromium/services/video_capture/testing_controls_impl.h @@ -5,19 +5,23 @@ #ifndef SERVICES_VIDEO_CAPTURE_TESTING_CONTROLS_IMPL_H_ #define SERVICES_VIDEO_CAPTURE_TESTING_CONTROLS_IMPL_H_ +#include "services/service_manager/public/cpp/service_context_ref.h" #include "services/video_capture/public/mojom/testing_controls.mojom.h" namespace video_capture { class TestingControlsImpl : public mojom::TestingControls { public: - TestingControlsImpl(); + TestingControlsImpl( + std::unique_ptr<service_manager::ServiceContextRef> service_ref); ~TestingControlsImpl() override; // mojom::TestingControls implementation. void Crash() override; private: + const std::unique_ptr<service_manager::ServiceContextRef> service_ref_; + DISALLOW_COPY_AND_ASSIGN(TestingControlsImpl); }; diff --git a/chromium/services/video_capture/texture_virtual_device_mojo_adapter.cc b/chromium/services/video_capture/texture_virtual_device_mojo_adapter.cc index 3239a4d74a6..42c604debfd 100644 --- a/chromium/services/video_capture/texture_virtual_device_mojo_adapter.cc +++ b/chromium/services/video_capture/texture_virtual_device_mojo_adapter.cc @@ -15,7 +15,9 @@ namespace video_capture { -TextureVirtualDeviceMojoAdapter::TextureVirtualDeviceMojoAdapter() = default; +TextureVirtualDeviceMojoAdapter::TextureVirtualDeviceMojoAdapter( + std::unique_ptr<service_manager::ServiceContextRef> service_ref) + : service_ref_(std::move(service_ref)) {} TextureVirtualDeviceMojoAdapter::~TextureVirtualDeviceMojoAdapter() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); diff --git a/chromium/services/video_capture/texture_virtual_device_mojo_adapter.h b/chromium/services/video_capture/texture_virtual_device_mojo_adapter.h index 2293ae05135..8f1749e5b46 100644 --- a/chromium/services/video_capture/texture_virtual_device_mojo_adapter.h +++ b/chromium/services/video_capture/texture_virtual_device_mojo_adapter.h @@ -8,6 +8,7 @@ #include "base/sequence_checker.h" #include "media/capture/video/video_capture_buffer_pool.h" #include "media/capture/video_capture_types.h" +#include "services/service_manager/public/cpp/service_context_ref.h" #include "services/video_capture/public/mojom/device.mojom.h" #include "services/video_capture/public/mojom/producer.mojom.h" #include "services/video_capture/public/mojom/receiver.mojom.h" @@ -18,7 +19,8 @@ namespace video_capture { class TextureVirtualDeviceMojoAdapter : public mojom::TextureVirtualDevice, public mojom::Device { public: - TextureVirtualDeviceMojoAdapter(); + explicit TextureVirtualDeviceMojoAdapter( + std::unique_ptr<service_manager::ServiceContextRef> service_ref); ~TextureVirtualDeviceMojoAdapter() override; void SetReceiverDisconnectedCallback(base::OnceClosure callback); @@ -48,6 +50,7 @@ class TextureVirtualDeviceMojoAdapter : public mojom::TextureVirtualDevice, private: void OnReceiverConnectionErrorOrClose(); + const std::unique_ptr<service_manager::ServiceContextRef> service_ref_; base::OnceClosure optional_receiver_disconnected_callback_; mojom::ReceiverPtr receiver_; std::unordered_map<int32_t, media::mojom::MailboxBufferHandleSetPtr> diff --git a/chromium/services/video_capture/texture_virtual_device_mojo_adapter_unittest.cc b/chromium/services/video_capture/texture_virtual_device_mojo_adapter_unittest.cc index f0774b7e5cd..4ec7f3bced3 100644 --- a/chromium/services/video_capture/texture_virtual_device_mojo_adapter_unittest.cc +++ b/chromium/services/video_capture/texture_virtual_device_mojo_adapter_unittest.cc @@ -17,14 +17,16 @@ namespace video_capture { class TextureVirtualDeviceMojoAdapterTest : public ::testing::Test { public: - TextureVirtualDeviceMojoAdapterTest() = default; + TextureVirtualDeviceMojoAdapterTest() + : service_keepalive_(nullptr, base::nullopt) {} void SetUp() override { mock_receiver_1_ = std::make_unique<MockReceiver>(mojo::MakeRequest(&receiver_1_)); mock_receiver_2_ = std::make_unique<MockReceiver>(mojo::MakeRequest(&receiver_2_)); - adapter_ = std::make_unique<TextureVirtualDeviceMojoAdapter>(); + adapter_ = std::make_unique<TextureVirtualDeviceMojoAdapter>( + service_keepalive_.CreateRef()); } protected: @@ -61,6 +63,7 @@ class TextureVirtualDeviceMojoAdapterTest : public ::testing::Test { private: base::test::ScopedTaskEnvironment task_environment_; + service_manager::ServiceKeepalive service_keepalive_; std::unique_ptr<TextureVirtualDeviceMojoAdapter> adapter_; mojom::ReceiverPtr receiver_1_; mojom::ReceiverPtr receiver_2_; diff --git a/chromium/services/video_capture/video_source_provider_impl.h b/chromium/services/video_capture/video_source_provider_impl.h index f7d0ecddbc9..468ae6d9ed0 100644 --- a/chromium/services/video_capture/video_source_provider_impl.h +++ b/chromium/services/video_capture/video_source_provider_impl.h @@ -51,6 +51,7 @@ class VideoSourceProviderImpl : public mojom::VideoSourceProvider { int client_count_ = 0; int closed_but_not_yet_disconnected_client_count_ = 0; mojo::BindingSet<mojom::VideoSourceProvider> bindings_; + std::unique_ptr<service_manager::ServiceContextRef> service_ref_; std::map<std::string, std::unique_ptr<VideoSourceImpl>> sources_; DISALLOW_COPY_AND_ASSIGN(VideoSourceProviderImpl); }; diff --git a/chromium/services/video_capture/virtual_device_enabled_device_factory.cc b/chromium/services/video_capture/virtual_device_enabled_device_factory.cc index cd413613cd2..6db39c737aa 100644 --- a/chromium/services/video_capture/virtual_device_enabled_device_factory.cc +++ b/chromium/services/video_capture/virtual_device_enabled_device_factory.cc @@ -92,6 +92,15 @@ VirtualDeviceEnabledDeviceFactory::VirtualDeviceEnabledDeviceFactory( VirtualDeviceEnabledDeviceFactory::~VirtualDeviceEnabledDeviceFactory() = default; +void VirtualDeviceEnabledDeviceFactory::SetServiceRef( + std::unique_ptr<service_manager::ServiceContextRef> service_ref) { + if (service_ref) + device_factory_->SetServiceRef(service_ref->Clone()); + else + device_factory_->SetServiceRef(nullptr); + service_ref_ = std::move(service_ref); +} + void VirtualDeviceEnabledDeviceFactory::GetDeviceInfos( GetDeviceInfosCallback callback) { device_factory_->GetDeviceInfos( @@ -143,7 +152,7 @@ void VirtualDeviceEnabledDeviceFactory::AddSharedMemoryVirtualDevice( OnVirtualDeviceProducerConnectionErrorOrClose, base::Unretained(this), device_id)); auto device = std::make_unique<SharedMemoryVirtualDeviceMojoAdapter>( - std::move(producer), + service_ref_->Clone(), std::move(producer), send_buffer_handles_to_producer_as_raw_file_descriptors); auto producer_binding = std::make_unique<mojo::Binding<mojom::SharedMemoryVirtualDevice>>( @@ -170,7 +179,8 @@ void VirtualDeviceEnabledDeviceFactory::AddTextureVirtualDevice( virtual_devices_by_id_.erase(virtual_device_iter); } - auto device = std::make_unique<TextureVirtualDeviceMojoAdapter>(); + auto device = + std::make_unique<TextureVirtualDeviceMojoAdapter>(service_ref_->Clone()); auto producer_binding = std::make_unique<mojo::Binding<mojom::TextureVirtualDevice>>( device.get(), std::move(virtual_device_request)); diff --git a/chromium/services/video_capture/virtual_device_enabled_device_factory.h b/chromium/services/video_capture/virtual_device_enabled_device_factory.h index 8c812c28f28..0d74f647055 100644 --- a/chromium/services/video_capture/virtual_device_enabled_device_factory.h +++ b/chromium/services/video_capture/virtual_device_enabled_device_factory.h @@ -9,6 +9,7 @@ #include <utility> #include "mojo/public/cpp/bindings/binding.h" +#include "services/service_manager/public/cpp/service_context_ref.h" #include "services/video_capture/device_factory.h" #include "services/video_capture/public/mojom/device.mojom.h" #include "services/video_capture/public/mojom/devices_changed_observer.mojom.h" @@ -28,6 +29,8 @@ class VirtualDeviceEnabledDeviceFactory : public DeviceFactory { ~VirtualDeviceEnabledDeviceFactory() override; // DeviceFactory implementation. + void SetServiceRef( + std::unique_ptr<service_manager::ServiceContextRef> service_ref) override; void GetDeviceInfos(GetDeviceInfosCallback callback) override; void CreateDevice(const std::string& device_id, mojom::DeviceRequest device_request, @@ -66,6 +69,7 @@ class VirtualDeviceEnabledDeviceFactory : public DeviceFactory { std::map<std::string, VirtualDeviceEntry> virtual_devices_by_id_; const std::unique_ptr<DeviceFactory> device_factory_; + std::unique_ptr<service_manager::ServiceContextRef> service_ref_; std::vector<mojom::DevicesChangedObserverPtr> devices_changed_observers_; base::WeakPtrFactory<VirtualDeviceEnabledDeviceFactory> weak_factory_; diff --git a/chromium/services/viz/public/cpp/compositing/shared_quad_state_struct_traits.h b/chromium/services/viz/public/cpp/compositing/shared_quad_state_struct_traits.h index 0d59e15f75d..fe9ba92c44f 100644 --- a/chromium/services/viz/public/cpp/compositing/shared_quad_state_struct_traits.h +++ b/chromium/services/viz/public/cpp/compositing/shared_quad_state_struct_traits.h @@ -63,10 +63,6 @@ struct StructTraits<viz::mojom::SharedQuadStateDataView, OptSharedQuadState> { static int32_t sorting_context_id(const OptSharedQuadState& input) { return input.sqs->sorting_context_id; } - - static bool is_fast_rounded_corner(const OptSharedQuadState& input) { - return input.sqs->is_fast_rounded_corner; - } }; template <> @@ -112,10 +108,6 @@ struct StructTraits<viz::mojom::SharedQuadStateDataView, viz::SharedQuadState> { return sqs.sorting_context_id; } - static bool is_fast_rounded_corner(const viz::SharedQuadState& sqs) { - return sqs.is_fast_rounded_corner; - } - static bool Read(viz::mojom::SharedQuadStateDataView data, viz::SharedQuadState* out) { if (!data.ReadQuadToTargetTransform(&out->quad_to_target_transform) || @@ -133,7 +125,6 @@ struct StructTraits<viz::mojom::SharedQuadStateDataView, viz::SharedQuadState> { return false; out->blend_mode = static_cast<SkBlendMode>(data.blend_mode()); out->sorting_context_id = data.sorting_context_id(); - out->is_fast_rounded_corner = data.is_fast_rounded_corner(); return true; } }; diff --git a/chromium/services/viz/public/interfaces/compositing/shared_quad_state.mojom b/chromium/services/viz/public/interfaces/compositing/shared_quad_state.mojom index 1ca6de822df..6ca90c7e47c 100644 --- a/chromium/services/viz/public/interfaces/compositing/shared_quad_state.mojom +++ b/chromium/services/viz/public/interfaces/compositing/shared_quad_state.mojom @@ -8,7 +8,6 @@ import "ui/gfx/geometry/mojo/geometry.mojom"; import "ui/gfx/mojo/rrect_f.mojom"; import "ui/gfx/mojo/transform.mojom"; -// See viz::SharedQuadState. struct SharedQuadState { // gfx.mojom.Transforms quad rects into the target content space. gfx.mojom.Transform quad_to_target_transform; @@ -37,6 +36,4 @@ struct SharedQuadState { // supported. uint32 blend_mode; int32 sorting_context_id; - - bool is_fast_rounded_corner; }; diff --git a/chromium/skia/ext/skia_commit_hash.h b/chromium/skia/ext/skia_commit_hash.h index 50656e0369f..57779c35afb 100644 --- a/chromium/skia/ext/skia_commit_hash.h +++ b/chromium/skia/ext/skia_commit_hash.h @@ -3,6 +3,6 @@ #ifndef SKIA_EXT_SKIA_COMMIT_HASH_H_ #define SKIA_EXT_SKIA_COMMIT_HASH_H_ -#define SKIA_COMMIT_HASH "2417cee95d9097a19d759a2267d4c3e51786e873" +#define SKIA_COMMIT_HASH "a10014304cba4f24b7af17191f59490faa8aee77" #endif // SKIA_EXT_SKIA_COMMIT_HASH_H_ diff --git a/chromium/third_party/blink/common/features.cc b/chromium/third_party/blink/common/features.cc index 6f58e10523e..91eec38eb8e 100644 --- a/chromium/third_party/blink/common/features.cc +++ b/chromium/third_party/blink/common/features.cc @@ -67,8 +67,18 @@ const base::Feature kBlinkGenPropertyTrees{"BlinkGenPropertyTrees", base::FEATURE_ENABLED_BY_DEFAULT}; // Enable a new CSS property called backdrop-filter. -const base::Feature kCSSBackdropFilter{"CSSBackdropFilter", - base::FEATURE_ENABLED_BY_DEFAULT}; +const base::Feature kCSSBackdropFilter { + "CSSBackdropFilter", +#if defined(OS_ANDROID) + // There are two bugs in the backdrop-filter feature for Android Webview + // only (crbug.com/990535 and crbug.com/991869). Because there is no + // compile-time flag for Webview, this disables all of Android, and the + // feature will be re-enabled for non-Webview Android by Finch. + base::FEATURE_DISABLED_BY_DEFAULT +#else + base::FEATURE_ENABLED_BY_DEFAULT +#endif +}; // Enable Display Locking JavaScript APIs. const base::Feature kDisplayLocking{"DisplayLocking", diff --git a/chromium/third_party/blink/renderer/controller/blink_initializer.cc b/chromium/third_party/blink/renderer/controller/blink_initializer.cc index 471f3623f74..e8558840bba 100644 --- a/chromium/third_party/blink/renderer/controller/blink_initializer.cc +++ b/chromium/third_party/blink/renderer/controller/blink_initializer.cc @@ -42,6 +42,7 @@ #include "third_party/blink/renderer/bindings/modules/v8/v8_context_snapshot_external_references.h" #include "third_party/blink/renderer/controller/blink_leak_detector.h" #include "third_party/blink/renderer/controller/dev_tools_frontend_impl.h" +#include "third_party/blink/renderer/core/animation/animation_clock.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/frame/display_cutout_client_impl.h" #include "third_party/blink/renderer/core/frame/local_frame.h" @@ -67,7 +68,9 @@ namespace { class EndOfTaskRunner : public Thread::TaskObserver { public: - void WillProcessTask(const base::PendingTask&) override {} + void WillProcessTask(const base::PendingTask&) override { + AnimationClock::NotifyTaskStart(); + } void DidProcessTask(const base::PendingTask&) override { // TODO(tzik): Move rejected promise handling to EventLoop. diff --git a/chromium/third_party/blink/renderer/core/BUILD.gn b/chromium/third_party/blink/renderer/core/BUILD.gn index 79a7f1d5010..6b856daa7e2 100644 --- a/chromium/third_party/blink/renderer/core/BUILD.gn +++ b/chromium/third_party/blink/renderer/core/BUILD.gn @@ -1452,6 +1452,7 @@ jumbo_source_set("unit_tests") { "page/slot_scoped_traversal_test.cc", "page/spatial_navigation_test.cc", "page/touch_adjustment_test.cc", + "page/validation_message_overlay_delegate_test.cc", "page/viewport_test.cc", "page/window_features_test.cc", "page/zoom_test.cc", diff --git a/chromium/third_party/blink/renderer/core/animation/animation_clock.cc b/chromium/third_party/blink/renderer/core/animation/animation_clock.cc index 1c8ae77d3e0..19ba57ea1e9 100644 --- a/chromium/third_party/blink/renderer/core/animation/animation_clock.cc +++ b/chromium/third_party/blink/renderer/core/animation/animation_clock.cc @@ -35,7 +35,18 @@ namespace blink { +namespace { +// This is an approximation of time between frames, used when ticking the +// animation clock outside of animation frame callbacks. +constexpr base::TimeDelta kApproximateFrameTime = + base::TimeDelta::FromSecondsD(1 / 60.0); +} // namespace + +unsigned AnimationClock::currently_running_task_ = 0; + void AnimationClock::UpdateTime(base::TimeTicks time) { + task_for_which_time_was_calculated_ = currently_running_task_; + // TODO(crbug.com/985770): Change this to a DCHECK_GE(time, time_) when // VR no longer sends historical timestamps. if (time < time_) @@ -43,7 +54,35 @@ void AnimationClock::UpdateTime(base::TimeTicks time) { time_ = time; } -base::TimeTicks AnimationClock::CurrentTime() const { +base::TimeTicks AnimationClock::CurrentTime() { + // By spec, within a single rendering lifecycle the AnimationClock time should + // not change (as it is set from the frame time). + if (!can_dynamically_update_time_) + return time_; + + // Outside of the rendering lifecycle, we may have to dynamically advance our + // own time (see comments on |SetAllowedToDynamicallyUpdateTime|). However we + // should never dynamically advance time inside a single task, as otherwise a + // single long-running JavaScript function could see multiple different times + // from document.timeline.currentTime. + if (task_for_which_time_was_calculated_ == currently_running_task_) + return time_; + + // Otherwise, we may need to dynamically update our own time. Again see the + // comments on |SetAllowedToDynamicallyUpdateTime|. + const base::TimeTicks current_time = clock_->NowTicks(); + base::TimeTicks new_time = time_; + if (time_ < current_time) { + // Attempt to predict what the most recent timestamp would have been. This + // may not produce a result greater than |time_|, but it greatly reduces the + // chance of conflicting with any future frame timestamp that does come in. + const base::TimeDelta frame_shift = + (current_time - time_) % kApproximateFrameTime; + new_time = current_time - frame_shift; + DCHECK_GE(new_time, time_); + } + UpdateTime(new_time); + return time_; } @@ -51,4 +90,11 @@ void AnimationClock::ResetTimeForTesting() { time_ = base::TimeTicks(); } +void AnimationClock::OverrideDynamicClockForTesting( + const base::TickClock* clock) { + clock_ = clock; + ResetTimeForTesting(); + UpdateTime(clock_->NowTicks()); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/animation/animation_clock.h b/chromium/third_party/blink/renderer/core/animation/animation_clock.h index b780210abdb..8f10d8b6852 100644 --- a/chromium/third_party/blink/renderer/core/animation/animation_clock.h +++ b/chromium/third_party/blink/renderer/core/animation/animation_clock.h @@ -34,6 +34,7 @@ #include <limits> #include "base/macros.h" +#include "base/time/default_tick_clock.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" @@ -46,14 +47,57 @@ class CORE_EXPORT AnimationClock { DISALLOW_NEW(); public: - AnimationClock() : time_() {} + AnimationClock() + : time_(), + can_dynamically_update_time_(false), + clock_(base::DefaultTickClock::GetInstance()), + task_for_which_time_was_calculated_( + std::numeric_limits<unsigned>::max()) {} void UpdateTime(base::TimeTicks time); - base::TimeTicks CurrentTime() const; + base::TimeTicks CurrentTime(); + + // The HTML spec says that the clock for animations is only updated once per + // rendering lifecycle, at the start. However the spec also assumes that the + // user agent runs rendering lifecycles constantly, back-to-back. In Blink we + // attempt to *not* run rendering lifecycles as much as possible, to avoid + // unnecessary CPU usage. + // + // As such, when outside a rendering lifecycle (for example, if a setInterval + // triggers) we allow the AnimationClock to dynamically adjust its time to + // look like it is being updated by the rendering lifecycles that never + // happened. + // + // TODO(crbug.com/995806): Allowing the AnimationClock to update itself is + // error prone. We should instead get the latest impl-frame time from the + // compositor when outside of a Blink rendering lifecycle (whilst still + // not changing within the same task). + void SetAllowedToDynamicallyUpdateTime(bool can_dynamically_update_time) { + can_dynamically_update_time_ = can_dynamically_update_time; + } + + // When using our dynamically update behavior outside rendering lifecycles, we + // still do not want the time to move forward within the same task (e.g. + // within a single setInterval callback). To achieve this we track the task in + // which the time was last updated, and don't update it again until we are in + // a new task. + static void NotifyTaskStart() { ++currently_running_task_; } + void ResetTimeForTesting(); + // The caller owns the passed in clock, which must outlive the AnimationClock. + void OverrideDynamicClockForTesting(const base::TickClock*); private: base::TimeTicks time_; + + // See |SetAllowedToDynamicallyUpdateTime| documentation for these members. + bool can_dynamically_update_time_; + const base::TickClock* clock_; + + // See |NotifyTaskStart| documentation for these members. + unsigned task_for_which_time_was_calculated_; + static unsigned currently_running_task_; + DISALLOW_COPY_AND_ASSIGN(AnimationClock); }; diff --git a/chromium/third_party/blink/renderer/core/animation/compositor_animations.cc b/chromium/third_party/blink/renderer/core/animation/compositor_animations.cc index be4f5e1fa50..a467082f74f 100644 --- a/chromium/third_party/blink/renderer/core/animation/compositor_animations.cc +++ b/chromium/third_party/blink/renderer/core/animation/compositor_animations.cc @@ -43,6 +43,7 @@ #include "third_party/blink/renderer/core/animation/element_animations.h" #include "third_party/blink/renderer/core/animation/keyframe_effect_model.h" #include "third_party/blink/renderer/core/dom/dom_node_ids.h" +#include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/layout/layout_box_model_object.h" #include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" @@ -343,8 +344,15 @@ CompositorAnimations::CheckCanStartElementOnCompositor( const Element& target_element) { FailureReasons reasons = kNoFailure; - if (!Platform::Current()->IsThreadedAnimationEnabled()) + // Both of these checks are required. It is legal to enable the compositor + // thread but disable threaded animations, and there are situations where + // threaded animations are enabled globally but this particular LocalFrame + // does not have a compositor (e.g. for overlays). + const Settings* settings = target_element.GetDocument().GetSettings(); + if ((settings && !settings->GetAcceleratedCompositingEnabled()) || + !Platform::Current()->IsThreadedAnimationEnabled()) { reasons |= kAcceleratedAnimationsDisabled; + } if (const auto* layout_object = target_element.GetLayoutObject()) { // We query paint property tree state below to determine whether the diff --git a/chromium/third_party/blink/renderer/core/animation/compositor_animations_test.cc b/chromium/third_party/blink/renderer/core/animation/compositor_animations_test.cc index 358dfd06f0b..bfaae4f10db 100644 --- a/chromium/third_party/blink/renderer/core/animation/compositor_animations_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/compositor_animations_test.cc @@ -1998,4 +1998,25 @@ TEST_P(AnimationCompositorAnimationsTest, EXPECT_EQ(host->CompositedAnimationsCount(), 0u); } +// Regression test for https://crbug.com/999333. We were relying on the Document +// always having Settings, which will not be the case if it is not attached to a +// Frame. +TEST_P(AnimationCompositorAnimationsTest, + DocumentWithoutSettingShouldNotCauseCrash) { + SetBodyInnerHTML("<div id='target'></div>"); + Element* target = GetElementById("target"); + ASSERT_TRUE(target); + + // Move the target element to another Document, that does not have a frame + // (and thus no Settings). + Document* another_document = MakeGarbageCollected<Document>(); + ASSERT_FALSE(another_document->GetSettings()); + + another_document->adoptNode(target, ASSERT_NO_EXCEPTION); + + // This should not crash. + EXPECT_NE(CheckCanStartElementOnCompositor(*target), + CompositorAnimations::kNoFailure); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/animation/document_timeline.cc b/chromium/third_party/blink/renderer/core/animation/document_timeline.cc index 7c19e649a3b..b6b0268ecd9 100644 --- a/chromium/third_party/blink/renderer/core/animation/document_timeline.cc +++ b/chromium/third_party/blink/renderer/core/animation/document_timeline.cc @@ -56,7 +56,7 @@ bool CompareAnimations(const Member<Animation>& left, // Returns the current animation time for a given |document|. This is // the animation clock time capped to be at least this document's // ZeroTime() such that the animation time is never negative when converted. -base::TimeTicks CurrentAnimationTime(const Document* document) { +base::TimeTicks CurrentAnimationTime(Document* document) { base::TimeTicks animation_time = document->GetAnimationClock().CurrentTime(); base::TimeTicks document_zero_time = document->Timeline().ZeroTime(); diff --git a/chromium/third_party/blink/renderer/core/animation/document_timeline_test.cc b/chromium/third_party/blink/renderer/core/animation/document_timeline_test.cc index d32736e9008..801ba257b80 100644 --- a/chromium/third_party/blink/renderer/core/animation/document_timeline_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/document_timeline_test.cc @@ -30,6 +30,7 @@ #include "third_party/blink/renderer/core/animation/document_timeline.h" +#include "base/test/simple_test_tick_clock.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/core/animation/animation_clock.h" @@ -68,6 +69,7 @@ class AnimationDocumentTimelineTest : public PageTestBase { PageTestBase::SetUp(IntSize()); document = &GetDocument(); GetAnimationClock().ResetTimeForTesting(); + GetAnimationClock().SetAllowedToDynamicallyUpdateTime(false); element = Element::Create(QualifiedName::Null(), document.Get()); platform_timing = MakeGarbageCollected<MockPlatformTiming>(); timeline = document->Timeline(); @@ -113,6 +115,7 @@ class AnimationDocumentTimelineRealTimeTest : public PageTestBase { PageTestBase::SetUp(IntSize()); document = &GetDocument(); timeline = document->Timeline(); + GetAnimationClock().SetAllowedToDynamicallyUpdateTime(false); } void TearDown() override { @@ -454,6 +457,39 @@ TEST_F(AnimationDocumentTimelineTest, PlayAfterDocumentDeref) { timeline->Play(keyframe_effect); } +// Regression test for https://crbug.com/995806, ensuring that we do dynamically +// progress the time when outside a rendering loop (so that we can serve e.g. +// setInterval), but also that we *only* dynamically progress the time when +// outside a rendering loop (so that we are mostly spec compliant). +TEST_F(AnimationDocumentTimelineTest, + PredictionBehaviorOnlyAppliesOutsideRenderingLoop) { + base::SimpleTestTickClock test_clock; + GetAnimationClock().OverrideDynamicClockForTesting(&test_clock); + ASSERT_EQ(GetAnimationClock().CurrentTime(), test_clock.NowTicks()); + + // As long as we are inside the rendering loop, we shouldn't update even + // across tasks. + base::TimeTicks before_time = GetAnimationClock().CurrentTime(); + test_clock.Advance(base::TimeDelta::FromSeconds(1)); + EXPECT_EQ(GetAnimationClock().CurrentTime(), before_time); + + AnimationClock::NotifyTaskStart(); + test_clock.Advance(base::TimeDelta::FromSeconds(1)); + EXPECT_EQ(GetAnimationClock().CurrentTime(), before_time); + + // Once we leave the rendering loop, however, it is valid for the time to + // increase *once* per task. + GetAnimationClock().SetAllowedToDynamicallyUpdateTime(true); + EXPECT_GT(GetAnimationClock().CurrentTime(), before_time); + + // The clock shouldn't tick again until we change task, however. + base::TimeTicks current_time = GetAnimationClock().CurrentTime(); + test_clock.Advance(base::TimeDelta::FromSeconds(1)); + EXPECT_EQ(GetAnimationClock().CurrentTime(), current_time); + AnimationClock::NotifyTaskStart(); + EXPECT_GT(GetAnimationClock().CurrentTime(), current_time); +} + // Ensure that origin time is correctly calculated even when the animation // clock has not yet been initialized. TEST_F(AnimationDocumentTimelineRealTimeTest, diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader.cc b/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader.cc index c63a32f28bb..27824b365c4 100644 --- a/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader.cc +++ b/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader.cc @@ -148,10 +148,12 @@ DOMArrayBuffer* FileReaderLoader::ArrayBufferResult() { if (!finished_loading_) { return DOMArrayBuffer::Create(ArrayBuffer::Create( - raw_data_->Data(), static_cast<unsigned>(bytes_loaded_))); + raw_data_.Data(), static_cast<unsigned>(bytes_loaded_))); } - array_buffer_result_ = DOMArrayBuffer::Create(std::move(raw_data_)); + WTF::ArrayBufferContents contents(std::move(raw_data_), + WTF::ArrayBufferContents::kNotShared); + array_buffer_result_ = DOMArrayBuffer::Create(contents); AdjustReportedMemoryUsageToV8(-1 * static_cast<int64_t>(bytes_loaded_)); raw_data_.reset(); return array_buffer_result_; @@ -171,7 +173,7 @@ String FileReaderLoader::StringResult() { // No conversion is needed. return string_result_; case kReadAsBinaryString: - SetStringResult(String(static_cast<const char*>(raw_data_->Data()), + SetStringResult(String(static_cast<const char*>(raw_data_.Data()), static_cast<size_t>(bytes_loaded_))); break; case kReadAsText: @@ -194,6 +196,17 @@ String FileReaderLoader::StringResult() { return string_result_; } +WTF::ArrayBufferContents::DataHandle FileReaderLoader::TakeDataHandle() { + if (!raw_data_ || error_code_ != FileErrorCode::kOK) + return WTF::ArrayBufferContents::DataHandle(); + + DCHECK(finished_loading_); + WTF::ArrayBufferContents::DataHandle handle = std::move(raw_data_); + AdjustReportedMemoryUsageToV8(-1 * static_cast<int64_t>(bytes_loaded_)); + raw_data_.reset(); + return handle; +} + void FileReaderLoader::SetEncoding(const String& encoding) { if (!encoding.IsEmpty()) encoding_ = WTF::TextEncoding(encoding); @@ -242,7 +255,9 @@ void FileReaderLoader::OnStartLoading(uint64_t total_bytes) { return; } - raw_data_ = ArrayBuffer::Create(static_cast<unsigned>(total_bytes), 1); + raw_data_ = WTF::ArrayBufferContents::CreateDataHandle( + static_cast<unsigned>(total_bytes), + WTF::ArrayBufferContents::kDontInitialize); if (!raw_data_) { Failed(FileErrorCode::kNotReadableErr, FailureType::kArrayBufferBuilderCreation); @@ -274,14 +289,14 @@ void FileReaderLoader::OnReceivedData(const char* data, unsigned data_length) { // that the BlobPtr is actually backed by a "real" blob, so to defend against // compromised renderer processes we still need to carefully validate anything // received. So return an error if we received too much data. - if (bytes_loaded_ + data_length > raw_data_->ByteLength()) { + if (bytes_loaded_ + data_length > raw_data_.DataLength()) { raw_data_.reset(); bytes_loaded_ = 0; Failed(FileErrorCode::kNotReadableErr, FailureType::kArrayBufferBuilderAppend); return; } - memcpy(static_cast<char*>(raw_data_->Data()) + bytes_loaded_, data, + memcpy(static_cast<char*>(raw_data_.Data()) + bytes_loaded_, data, data_length); bytes_loaded_ += data_length; is_raw_data_converted_ = false; @@ -293,7 +308,7 @@ void FileReaderLoader::OnReceivedData(const char* data, unsigned data_length) { void FileReaderLoader::OnFinishLoading() { if (read_type_ != kReadByClient && raw_data_) { - DCHECK_EQ(bytes_loaded_, raw_data_->ByteLength()); + DCHECK_EQ(bytes_loaded_, raw_data_.DataLength()); is_raw_data_converted_ = false; } @@ -436,7 +451,7 @@ String FileReaderLoader::ConvertToText() { TextResourceDecoderOptions::kPlainTextContent, encoding_.IsValid() ? encoding_ : UTF8Encoding())); } - builder.Append(decoder_->Decode(static_cast<const char*>(raw_data_->Data()), + builder.Append(decoder_->Decode(static_cast<const char*>(raw_data_.Data()), static_cast<size_t>(bytes_loaded_))); if (finished_loading_) @@ -462,7 +477,7 @@ String FileReaderLoader::ConvertToDataURL() { builder.Append(";base64,"); Vector<char> out; - Base64Encode(base::make_span(static_cast<const uint8_t*>(raw_data_->Data()), + Base64Encode(base::make_span(static_cast<const uint8_t*>(raw_data_.Data()), SafeCast<unsigned>(bytes_loaded_)), out); builder.Append(out.data(), out.size()); diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader.h b/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader.h index d460a3f50c9..5bf738e4b2b 100644 --- a/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader.h +++ b/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader.h @@ -44,6 +44,7 @@ #include "third_party/blink/renderer/platform/wtf/text/text_encoding.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h" +#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h" namespace blink { @@ -85,6 +86,7 @@ class CORE_EXPORT FileReaderLoader : public mojom::blink::BlobReaderClient { DOMArrayBuffer* ArrayBufferResult(); String StringResult(); + WTF::ArrayBufferContents::DataHandle TakeDataHandle(); // Returns the total bytes received. Bytes ignored by m_rawData won't be // counted. @@ -154,7 +156,7 @@ class CORE_EXPORT FileReaderLoader : public mojom::blink::BlobReaderClient { WTF::TextEncoding encoding_; String data_type_; - scoped_refptr<ArrayBuffer> raw_data_; + WTF::ArrayBufferContents::DataHandle raw_data_; bool is_raw_data_converted_ = false; Persistent<DOMArrayBuffer> array_buffer_result_; diff --git a/chromium/third_party/blink/renderer/core/frame/frame_overlay.cc b/chromium/third_party/blink/renderer/core/frame/frame_overlay.cc index 775930a98f0..f39011767f9 100644 --- a/chromium/third_party/blink/renderer/core/frame/frame_overlay.cc +++ b/chromium/third_party/blink/renderer/core/frame/frame_overlay.cc @@ -119,6 +119,11 @@ void FrameOverlay::PaintContents(const GraphicsLayer* graphics_layer, Paint(context); } +void FrameOverlay::ServiceScriptedAnimations( + base::TimeTicks monotonic_frame_begin_time) { + delegate_->ServiceScriptedAnimations(monotonic_frame_begin_time); +} + String FrameOverlay::DebugName(const GraphicsLayer*) const { DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()); return "Frame Overlay Content Layer"; diff --git a/chromium/third_party/blink/renderer/core/frame/frame_overlay.h b/chromium/third_party/blink/renderer/core/frame/frame_overlay.h index 267312c66de..ae0df9d1353 100644 --- a/chromium/third_party/blink/renderer/core/frame/frame_overlay.h +++ b/chromium/third_party/blink/renderer/core/frame/frame_overlay.h @@ -56,6 +56,10 @@ class CORE_EXPORT FrameOverlay : public GraphicsLayerClient, // For CompositeAfterPaint. Invalidates composited layers managed by the // delegate if any. virtual void Invalidate() {} + + // Service any animations managed by the delegate. + virtual void ServiceScriptedAnimations( + base::TimeTicks monotonic_frame_begin_time) {} }; FrameOverlay(LocalFrame*, std::unique_ptr<FrameOverlay::Delegate>); @@ -76,6 +80,9 @@ class CORE_EXPORT FrameOverlay : public GraphicsLayerClient, const Delegate* GetDelegate() const { return delegate_.get(); } const LocalFrame& Frame() const { return *frame_; } + // Services any animations that the overlay may be managing. + void ServiceScriptedAnimations(base::TimeTicks monotonic_frame_begin_time); + // DisplayItemClient methods. String DebugName() const final { return "FrameOverlay"; } IntRect VisualRect() const override; diff --git a/chromium/third_party/blink/renderer/core/frame/local_frame.cc b/chromium/third_party/blink/renderer/core/frame/local_frame.cc index 1f5407c50ed..bcc0869cc2f 100644 --- a/chromium/third_party/blink/renderer/core/frame/local_frame.cc +++ b/chromium/third_party/blink/renderer/core/frame/local_frame.cc @@ -1661,6 +1661,10 @@ void LocalFrame::UnpauseContext() { } void LocalFrame::SetLifecycleState(mojom::FrameLifecycleState state) { + // Don't allow lifecycle state changes for detached frames. + if (!IsAttached()) + return; + // If we have asked to be frozen we will only do this once the // load event has fired. if ((state == mojom::FrameLifecycleState::kFrozen || @@ -1688,13 +1692,21 @@ void LocalFrame::SetLifecycleState(mojom::FrameLifecycleState state) { lifecycle_state_ = state; if (freeze) { - if (lifecycle_state_ != mojom::FrameLifecycleState::kPaused) + if (lifecycle_state_ != mojom::FrameLifecycleState::kPaused) { DidFreeze(); + // DidFreeze can dispatch JS events, causing |this| to be detached. + if (!IsAttached()) + return; + } PauseContext(); } else { UnpauseContext(); - if (old_state != mojom::FrameLifecycleState::kPaused) + if (old_state != mojom::FrameLifecycleState::kPaused) { DidResume(); + // DidResume can dispatch JS events, causing |this| to be detached. + if (!IsAttached()) + return; + } } if (Client()) Client()->LifecycleStateChanged(state); diff --git a/chromium/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc b/chromium/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc index 096a501994e..00fa7e0abcb 100644 --- a/chromium/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc +++ b/chromium/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc @@ -907,6 +907,7 @@ class LazyLoadAutomaticImagesTest : public SimTest { ScrollOffset(0, kLoadingDistanceThreshold + kViewportHeight), kProgrammaticScroll); Compositor().BeginFrame(); + test::RunPendingTasks(); full_resource.Complete(ReadTestImage()); ExpectResourceIsFullImage(GetDocument().Fetcher()->CachedResource( KURL("https://example.com/image.png"))); @@ -1349,6 +1350,7 @@ TEST_F(LazyLoadAutomaticImagesTest, LazyLoadDisabledOnReload) { LazyLoadAutomaticImagesTest::kViewportHeight), kProgrammaticScroll); Compositor().BeginFrame(); + test::RunPendingTasks(); auto_image.Complete(ReadTestImage()); lazy_image.Complete(ReadTestImage()); test::RunPendingTasks(); @@ -1376,6 +1378,7 @@ TEST_F(LazyLoadAutomaticImagesTest, LazyLoadDisabledOnReload) { SimSubresourceRequest lazy_image("https://example.com/image_lazy.png", "image/png"); Compositor().BeginFrame(); + test::RunPendingTasks(); lazy_image.Complete(ReadTestImage()); test::RunPendingTasks(); histogram_tester.ExpectTotalCount( diff --git a/chromium/third_party/blink/renderer/core/imagebitmap/image_bitmap_factories.cc b/chromium/third_party/blink/renderer/core/imagebitmap/image_bitmap_factories.cc index 338eabd4ad2..0f73d79e946 100644 --- a/chromium/third_party/blink/renderer/core/imagebitmap/image_bitmap_factories.cc +++ b/chromium/third_party/blink/renderer/core/imagebitmap/image_bitmap_factories.cc @@ -49,7 +49,6 @@ #include "third_party/blink/renderer/core/svg/svg_image_element.h" #include "third_party/blink/renderer/core/workers/worker_global_scope.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" -#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h" #include "third_party/blink/renderer/platform/instrumentation/histogram.h" #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" @@ -298,64 +297,67 @@ void ImageBitmapFactories::ImageBitmapLoader::ContextDestroyed( } void ImageBitmapFactories::ImageBitmapLoader::DidFinishLoading() { - DOMArrayBuffer* array_buffer = loader_->ArrayBufferResult(); + auto data_handle = loader_->TakeDataHandle(); loader_.reset(); - if (!array_buffer) { + if (!data_handle) { RejectPromise(kAllocationFailureImageBitmapRejectionReason); return; } - ScheduleAsyncImageBitmapDecoding(array_buffer); + ScheduleAsyncImageBitmapDecoding(std::move(data_handle)); } void ImageBitmapFactories::ImageBitmapLoader::DidFail(FileErrorCode) { RejectPromise(kUndecodableImageBitmapRejectionReason); } -void ImageBitmapFactories::ImageBitmapLoader::ScheduleAsyncImageBitmapDecoding( - DOMArrayBuffer* array_buffer) { - scoped_refptr<base::SingleThreadTaskRunner> task_runner = - Thread::Current()->GetTaskRunner(); - worker_pool::PostTask( - FROM_HERE, - CrossThreadBindOnce( - &ImageBitmapFactories::ImageBitmapLoader::DecodeImageOnDecoderThread, - WrapCrossThreadPersistent(this), std::move(task_runner), - WrapCrossThreadPersistent(array_buffer), options_->premultiplyAlpha(), - options_->colorSpaceConversion())); -} - -void ImageBitmapFactories::ImageBitmapLoader::DecodeImageOnDecoderThread( +namespace { +void DecodeImageOnDecoderThread( scoped_refptr<base::SingleThreadTaskRunner> task_runner, - DOMArrayBuffer* array_buffer, - const String& premultiply_alpha_option, - const String& color_space_conversion_option) { - DCHECK(!IsMainThread()); - - ImageDecoder::AlphaOption alpha_op = ImageDecoder::kAlphaPremultiplied; - if (premultiply_alpha_option == "none") - alpha_op = ImageDecoder::kAlphaNotPremultiplied; - bool ignore_color_space = false; - if (color_space_conversion_option == "none") - ignore_color_space = true; + WTF::ArrayBufferContents::DataHandle data_handle, + ImageDecoder::AlphaOption alpha_option, + ColorBehavior color_behavior, + WTF::CrossThreadOnceFunction<void(sk_sp<SkImage>)> result_callback) { const bool data_complete = true; std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create( SegmentReader::CreateFromSkData(SkData::MakeWithoutCopy( - array_buffer->Data(), array_buffer->ByteLength())), - data_complete, alpha_op, ImageDecoder::kDefaultBitDepth, - ignore_color_space ? ColorBehavior::Ignore() : ColorBehavior::Tag()); + data_handle.Data(), data_handle.DataLength())), + data_complete, alpha_option, ImageDecoder::kDefaultBitDepth, + color_behavior); sk_sp<SkImage> frame; if (decoder) { frame = ImageBitmap::GetSkImageFromDecoder(std::move(decoder)); } PostCrossThreadTask( *task_runner, FROM_HERE, - CrossThreadBindOnce(&ImageBitmapFactories::ImageBitmapLoader:: - ResolvePromiseOnOriginalThread, - WrapCrossThreadPersistent(this), std::move(frame))); + CrossThreadBindOnce(std::move(result_callback), std::move(frame))); +} +} // namespace + +void ImageBitmapFactories::ImageBitmapLoader::ScheduleAsyncImageBitmapDecoding( + WTF::ArrayBufferContents::DataHandle data_handle) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + scoped_refptr<base::SingleThreadTaskRunner> task_runner = + Thread::Current()->GetTaskRunner(); + ImageDecoder::AlphaOption alpha_option = + options_->premultiplyAlpha() != "none" + ? ImageDecoder::AlphaOption::kAlphaPremultiplied + : ImageDecoder::AlphaOption::kAlphaNotPremultiplied; + ColorBehavior color_behavior = options_->colorSpaceConversion() == "none" + ? ColorBehavior::Ignore() + : ColorBehavior::Tag(); + worker_pool::PostTask( + FROM_HERE, + CrossThreadBindOnce( + DecodeImageOnDecoderThread, std::move(task_runner), + std::move(data_handle), alpha_option, color_behavior, + CrossThreadBindOnce(&ImageBitmapFactories::ImageBitmapLoader:: + ResolvePromiseOnOriginalThread, + WrapCrossThreadWeakPersistent(this)))); } void ImageBitmapFactories::ImageBitmapLoader::ResolvePromiseOnOriginalThread( sk_sp<SkImage> frame) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!frame) { RejectPromise(kUndecodableImageBitmapRejectionReason); return; diff --git a/chromium/third_party/blink/renderer/core/imagebitmap/image_bitmap_factories.h b/chromium/third_party/blink/renderer/core/imagebitmap/image_bitmap_factories.h index 503364df46b..465ccbfa787 100644 --- a/chromium/third_party/blink/renderer/core/imagebitmap/image_bitmap_factories.h +++ b/chromium/third_party/blink/renderer/core/imagebitmap/image_bitmap_factories.h @@ -47,6 +47,7 @@ #include "third_party/blink/renderer/platform/bindings/name_client.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/geometry/int_rect.h" +#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h" #include "third_party/blink/renderer/platform/supplementable.h" #include "third_party/skia/include/core/SkRefCnt.h" @@ -129,6 +130,8 @@ class ImageBitmapFactories final ~ImageBitmapLoader() override; private: + SEQUENCE_CHECKER(sequence_checker_); + enum ImageBitmapRejectionReason { kUndecodableImageBitmapRejectionReason, kAllocationFailureImageBitmapRejectionReason, @@ -136,12 +139,7 @@ class ImageBitmapFactories final void RejectPromise(ImageBitmapRejectionReason); - void ScheduleAsyncImageBitmapDecoding(DOMArrayBuffer*); - void DecodeImageOnDecoderThread( - scoped_refptr<base::SingleThreadTaskRunner>, - DOMArrayBuffer*, - const String& premultiply_alpha_option, - const String& color_space_conversion_option); + void ScheduleAsyncImageBitmapDecoding(WTF::ArrayBufferContents::DataHandle); void ResolvePromiseOnOriginalThread(sk_sp<SkImage>); // ContextLifecycleObserver diff --git a/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.cc index 4fcefb3de1d..160d6e2a19b 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.cc @@ -499,12 +499,6 @@ LayoutUnit LayoutFlexibleBox::ChildUnstretchedLogicalHeight( // This should only be called if the logical height is the cross size DCHECK(MainAxisIsInlineAxis(child)); if (NeedToStretchChildLogicalHeight(child)) { - LayoutUnit old_override_height = LayoutUnit(-1); - if (child.HasOverrideLogicalHeight()) { - old_override_height = child.OverrideLogicalHeight(); - const_cast<LayoutBox&>(child).ClearOverrideLogicalHeight(); - } - LayoutUnit child_intrinsic_content_logical_height; if (!child.ShouldApplySizeContainment()) { if (child.DisplayLockInducesSizeContainment()) { @@ -522,10 +516,6 @@ LayoutUnit LayoutFlexibleBox::ChildUnstretchedLogicalHeight( LogicalExtentComputedValues values; child.ComputeLogicalHeight(child_intrinsic_logical_height, LayoutUnit(), values); - if (old_override_height != LayoutUnit(-1)) { - const_cast<LayoutBox&>(child).SetOverrideLogicalHeight( - old_override_height); - } return values.extent_; } return child.LogicalHeight(); @@ -536,23 +526,14 @@ LayoutUnit LayoutFlexibleBox::ChildUnstretchedLogicalWidth( const LayoutBox& child) const { // This should only be called if the logical width is the cross size DCHECK(!MainAxisIsInlineAxis(child)); - + DCHECK(!child.HasOverrideLogicalWidth()); // We compute the width as if we were unstretched. Only the main axis // override size is set at this point. // However, if our cross axis length is definite we don't need to recompute // and can just return the already-set logical width. if (!CrossAxisLengthIsDefinite(child, child.StyleRef().LogicalWidth())) { - LayoutUnit old_override_width = LayoutUnit(-1); - if (child.HasOverrideLogicalWidth()) { - old_override_width = child.OverrideLogicalWidth(); - const_cast<LayoutBox&>(child).ClearOverrideLogicalWidth(); - } - LogicalExtentComputedValues values; child.ComputeLogicalWidth(values); - - if (old_override_width != LayoutUnit(-1)) - const_cast<LayoutBox&>(child).SetOverrideLogicalWidth(old_override_width); return values.extent_; } @@ -1223,6 +1204,8 @@ void LayoutFlexibleBox::ConstructAndAppendFlexItem( FlexLayoutAlgorithm* algorithm, LayoutBox& child, ChildLayoutType layout_type) { + if (layout_type != kNeverLayout) + child.ClearOverrideSize(); if (layout_type != kNeverLayout && ChildHasIntrinsicMainAxisSize(*algorithm, child)) { // If this condition is true, then ComputeMainAxisExtentForChild will call @@ -1237,7 +1220,6 @@ void LayoutFlexibleBox::ConstructAndAppendFlexItem( UpdateBlockChildDirtyBitsBeforeLayout(layout_type == kForceLayout, child); if (child.NeedsLayout() || layout_type == kForceLayout || !intrinsic_size_along_main_axis_.Contains(&child)) { - child.ClearOverrideSize(); child.ForceLayout(); CacheChildMainSize(child); } @@ -1504,19 +1486,8 @@ void LayoutFlexibleBox::LayoutLineItems(FlexLine* current_line, UpdateBlockChildDirtyBitsBeforeLayout(force_child_relayout, *child); if (!child->NeedsLayout()) MarkChildForPaginationRelayoutIfNeeded(*child, layout_scope); - if (child->NeedsLayout()) { + if (child->NeedsLayout()) relaid_out_children_.insert(child); - // It is very important that we only clear the cross axis override size - // if we are in fact going to lay out the child. Otherwise, the cross - // axis size and the actual laid out size get out of sync, which will - // cause problems if we later lay out the child in simplified layout, - // which does not go through regular flex layout and therefore would - // not reset the cross axis size. - if (MainAxisIsInlineAxis(*child)) - child->ClearOverrideLogicalHeight(); - else - child->ClearOverrideLogicalWidth(); - } child->LayoutIfNeeded(); // This shouldn't be necessary, because we set the override size to be diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc index a3d84fb4390..e73dbce4bd4 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc @@ -456,6 +456,8 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendText( // If not create a new item as needed. if (UNLIKELY(layout_text->IsWordBreak())) { + typename OffsetMappingBuilder::SourceNodeScope scope(&mapping_builder_, + layout_text); AppendBreakOpportunity(layout_text); return; } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc index 39d7c563bf0..e3dfe0194be 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc @@ -1485,16 +1485,19 @@ void NGLineBreaker::HandleCloseTag(const NGInlineItem& item, MoveToNextOf(item); // If the line can break after the previous item, prohibit it and allow break - // after this close tag instead. - if (was_auto_wrap) { - const NGInlineItemResults& item_results = line_info->Results(); - if (item_results.size() >= 2) { - NGInlineItemResult* last = std::prev(item_result); + // after this close tag instead. Even when the close tag has "nowrap", break + // after it is allowed if the line is breakable after the previous item. + const NGInlineItemResults& item_results = line_info->Results(); + if (item_results.size() >= 2) { + NGInlineItemResult* last = std::prev(item_result); + if (was_auto_wrap || last->can_break_after) { item_result->can_break_after = last->can_break_after; last->can_break_after = false; + return; } - return; } + if (was_auto_wrap) + return; DCHECK(!item_result->can_break_after); if (!auto_wrap_) @@ -1513,13 +1516,14 @@ void NGLineBreaker::HandleCloseTag(const NGInlineItem& item, ComputeCanBreakAfter(item_result, auto_wrap_, break_iterator_); } +// Returns whether this item contains only spaces that can hang. bool NGLineBreaker::ShouldHangTraillingSpaces(const NGInlineItem& item) { if (!item.Length()) return false; const ComputedStyle& style = *item.Style(); - if (!auto_wrap_ || (!style.CollapseWhiteSpace() && - style.WhiteSpace() == EWhiteSpace::kBreakSpaces)) + if (!style.AutoWrap() || (!style.CollapseWhiteSpace() && + style.WhiteSpace() == EWhiteSpace::kBreakSpaces)) return false; const String& text = Text(); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc index f8895564880..9e5ef9dc4aa 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc @@ -107,7 +107,8 @@ void NGOffsetMappingUnit::AssertValid() const { SECURITY_DCHECK(dom_start_ <= dom_end_) << dom_start_ << " vs. " << dom_end_; SECURITY_DCHECK(text_content_start_ <= text_content_end_) << text_content_start_ << " vs. " << text_content_end_; - if (layout_object_->IsText()) { + if (layout_object_->IsText() && + !ToLayoutText(*layout_object_).IsWordBreak()) { const LayoutText& layout_text = ToLayoutText(*layout_object_); const unsigned text_start = AssociatedNode() ? layout_text.TextStartOffset() : 0; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc index 533fb646428..206226f2b87 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc @@ -1452,6 +1452,26 @@ TEST_F(NGOffsetMappingTest, EndOfLastNonCollapsedContentWithPseudo) { EXPECT_EQ(Position(), GetOffsetMapping().EndOfLastNonCollapsedContent(position)); } + +TEST_F(NGOffsetMappingTest, WordBreak) { + SetupHtml("t", "<div id=t>a<wbr>b</div>"); + + const LayoutObject& text_a = *layout_object_; + const LayoutObject& wbr = *text_a.NextSibling(); + const LayoutObject& text_b = *wbr.NextSibling(); + const NGOffsetMapping& result = GetOffsetMapping(); + + EXPECT_EQ((Vector<NGOffsetMappingUnit>{ + NGOffsetMappingUnit(kIdentity, text_a, 0u, 1u, 0u, 1u), + NGOffsetMappingUnit(kIdentity, wbr, 0u, 1u, 1u, 2u), + NGOffsetMappingUnit(kIdentity, text_b, 0u, 1u, 2u, 3u)}), + result.GetUnits()); + + EXPECT_EQ((Vector<NGOffsetMappingUnit>{ + NGOffsetMappingUnit(kIdentity, wbr, 0u, 1u, 1u, 2u)}), + result.GetMappingUnitsForLayoutObject(wbr)); +} + // Test |GetOffsetMapping| which is available both for LayoutNG and for legacy. class NGOffsetMappingGetterTest : public RenderingTest, public testing::WithParamInterface<bool>, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc index 2c37e28cbdd..aeaa0c8ba9a 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc @@ -84,7 +84,7 @@ PhysicalRect NGPhysicalLineBoxFragment::ScrollableOverflow( PhysicalSize container_physical_size) const { WritingMode container_writing_mode = container_style->GetWritingMode(); TextDirection container_direction = container_style->Direction(); - PhysicalRect overflow({}, Size()); + PhysicalRect overflow; for (const auto& child : Children()) { PhysicalRect child_scroll_overflow = child->ScrollableOverflowForPropagation(container); @@ -116,6 +116,15 @@ PhysicalRect NGPhysicalLineBoxFragment::ScrollableOverflow( } overflow.Unite(child_scroll_overflow); } + + // Make sure we include the inline-size of the line-box in the overflow. + PhysicalRect rect; + if (IsHorizontalWritingMode(container_writing_mode)) + rect.size.width = Size().width; + else + rect.size.height = Size().height; + overflow.UniteEvenIfEmpty(rect); + return overflow; } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc index 15980a27bae..b2e4a86c335 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc @@ -729,7 +729,12 @@ void NGBlockNode::CopyFragmentDataToLayoutBox( if (LIKELY(IsLastFragment(physical_fragment))) intrinsic_content_logical_height -= border_scrollbar_padding.BlockSum(); - box_->SetIntrinsicContentLogicalHeight(intrinsic_content_logical_height); + if (!constraint_space.IsFixedSizeBlock()) { + // If we had a fixed block size, our children will have sized themselves + // relative to the fixed size, which would make our intrinsic size + // incorrect (too big). + box_->SetIntrinsicContentLogicalHeight(intrinsic_content_logical_height); + } // TODO(mstensho): This should always be done by the parent algorithm, since // we may have auto margins, which only the parent is able to resolve. Remove diff --git a/chromium/third_party/blink/renderer/core/loader/image_loader.cc b/chromium/third_party/blink/renderer/core/loader/image_loader.cc index 2f53751c117..ac5b8fee680 100644 --- a/chromium/third_party/blink/renderer/core/loader/image_loader.cc +++ b/chromium/third_party/blink/renderer/core/loader/image_loader.cc @@ -668,7 +668,18 @@ void ImageLoader::DoUpdateFromElement( params.SetLazyImageAutoReload(); } - new_image_content = ImageResourceContent::Fetch(params, document.Fetcher()); + if (lazy_image_load_state_ == LazyImageLoadState::kDeferred && + was_fully_deferred_) { + // TODO(rajendrant): Remove this temporary workaround of creating a 1x1 + // placeholder to fix an intersection observer issue not firing with + // certain styles (https://crbug.com/992765). Instead + // NoImageResourceToLoad() should be skipped when the image is deferred. + // https://crbug.com/999209 + new_image_content = ImageResourceContent::CreateLazyImagePlaceholder(); + } else { + new_image_content = + ImageResourceContent::Fetch(params, document.Fetcher()); + } // If this load is starting while navigating away, treat it as an auditing // keepalive request, and don't report its results back to the element. diff --git a/chromium/third_party/blink/renderer/core/loader/resource/image_resource_content.cc b/chromium/third_party/blink/renderer/core/loader/resource/image_resource_content.cc index 04b8d7dfc2f..b277d667a12 100644 --- a/chromium/third_party/blink/renderer/core/loader/resource/image_resource_content.cc +++ b/chromium/third_party/blink/renderer/core/loader/resource/image_resource_content.cc @@ -119,6 +119,14 @@ ImageResourceContent* ImageResourceContent::CreateLoaded( return content; } +ImageResourceContent* ImageResourceContent::CreateLazyImagePlaceholder() { + ImageResourceContent* content = MakeGarbageCollected<ImageResourceContent>(); + content->content_status_ = ResourceStatus::kCached; + content->image_ = + PlaceholderImage::CreateForLazyImages(content, IntSize(1, 1)); + return content; +} + ImageResourceContent* ImageResourceContent::Fetch(FetchParameters& params, ResourceFetcher* fetcher) { // TODO(hiroshige): Remove direct references to ImageResource by making diff --git a/chromium/third_party/blink/renderer/core/loader/resource/image_resource_content.h b/chromium/third_party/blink/renderer/core/loader/resource/image_resource_content.h index 7c91be4f723..c022783bd1a 100644 --- a/chromium/third_party/blink/renderer/core/loader/resource/image_resource_content.h +++ b/chromium/third_party/blink/renderer/core/loader/resource/image_resource_content.h @@ -56,6 +56,8 @@ class CORE_EXPORT ImageResourceContent final // Creates ImageResourceContent from an already loaded image. static ImageResourceContent* CreateLoaded(scoped_refptr<blink::Image>); + static ImageResourceContent* CreateLazyImagePlaceholder(); + static ImageResourceContent* Fetch(FetchParameters&, ResourceFetcher*); explicit ImageResourceContent(scoped_refptr<blink::Image> = nullptr); diff --git a/chromium/third_party/blink/renderer/core/page/DEPS b/chromium/third_party/blink/renderer/core/page/DEPS new file mode 100644 index 00000000000..a45c5f7cb81 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/page/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + # For validation_message_overlay_delegate_test.cc + "+base/strings/utf_string_conversions.h", +] diff --git a/chromium/third_party/blink/renderer/core/page/page_animator.cc b/chromium/third_party/blink/renderer/core/page/page_animator.cc index 42ea986e571..4a16ce2d429 100644 --- a/chromium/third_party/blink/renderer/core/page/page_animator.cc +++ b/chromium/third_party/blink/renderer/core/page/page_animator.cc @@ -30,6 +30,10 @@ void PageAnimator::Trace(blink::Visitor* visitor) { void PageAnimator::ServiceScriptedAnimations( base::TimeTicks monotonic_animation_start_time) { base::AutoReset<bool> servicing(&servicing_animations_, true); + + // Once we are inside a frame's lifecycle, the AnimationClock should hold its + // time value until the end of the frame. + Clock().SetAllowedToDynamicallyUpdateTime(false); Clock().UpdateTime(monotonic_animation_start_time); HeapVector<Member<Document>, 32> documents; @@ -84,7 +88,7 @@ void PageAnimator::ServiceScriptedAnimations( page_->GetValidationMessageClient().LayoutOverlay(); } -void PageAnimator::RunPostAnimationFrameCallbacks() { +void PageAnimator::PostAnimate() { HeapVector<Member<Document>, 32> documents; for (Frame* frame = page_->MainFrame(); frame; frame = frame->Tree().TraverseNext()) { @@ -92,8 +96,21 @@ void PageAnimator::RunPostAnimationFrameCallbacks() { documents.push_back(To<LocalFrame>(frame)->GetDocument()); } + // Run the post-animation frame callbacks. See + // https://github.com/WICG/requestPostAnimationFrame for (auto& document : documents) document->RunPostAnimationFrameCallbacks(); + + // If we don't have an imminently incoming frame, we need to let the + // AnimationClock update its own time to properly service out-of-lifecycle + // events such as setInterval (see https://crbug.com/995806). This isn't a + // perfect heuristic, but at the very least we know that if there is a pending + // RAF we will be getting a new frame and thus don't need to unlock the clock. + bool next_frame_has_raf = false; + for (auto& document : documents) + next_frame_has_raf |= document->NextFrameHasPendingRAF(); + if (!next_frame_has_raf) + Clock().SetAllowedToDynamicallyUpdateTime(true); } void PageAnimator::SetSuppressFrameRequestsWorkaroundFor704763Only( diff --git a/chromium/third_party/blink/renderer/core/page/page_animator.h b/chromium/third_party/blink/renderer/core/page/page_animator.h index 0e2616f8385..84701414048 100644 --- a/chromium/third_party/blink/renderer/core/page/page_animator.h +++ b/chromium/third_party/blink/renderer/core/page/page_animator.h @@ -23,7 +23,7 @@ class CORE_EXPORT PageAnimator final : public GarbageCollected<PageAnimator> { void ScheduleVisualUpdate(LocalFrame*); void ServiceScriptedAnimations( base::TimeTicks monotonic_animation_start_time); - void RunPostAnimationFrameCallbacks(); + void PostAnimate(); bool IsServicingAnimations() const { return servicing_animations_; } diff --git a/chromium/third_party/blink/renderer/core/page/page_widget_delegate.cc b/chromium/third_party/blink/renderer/core/page/page_widget_delegate.cc index d0777576a07..37f1a9d0cab 100644 --- a/chromium/third_party/blink/renderer/core/page/page_widget_delegate.cc +++ b/chromium/third_party/blink/renderer/core/page/page_widget_delegate.cc @@ -40,6 +40,7 @@ #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/page/autoscroll_controller.h" #include "third_party/blink/renderer/core/page/page.h" +#include "third_party/blink/renderer/core/page/validation_message_client.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/wtf/time.h" @@ -49,10 +50,15 @@ void PageWidgetDelegate::Animate(Page& page, base::TimeTicks monotonic_frame_begin_time) { page.GetAutoscrollController().Animate(); page.Animator().ServiceScriptedAnimations(monotonic_frame_begin_time); + // The ValidationMessage overlay manages its own internal Page that isn't + // hooked up the normal BeginMainFrame flow, so we manually tick its + // animations here. + page.GetValidationMessageClient().ServiceScriptedAnimations( + monotonic_frame_begin_time); } void PageWidgetDelegate::PostAnimate(Page& page) { - page.Animator().RunPostAnimationFrameCallbacks(); + page.Animator().PostAnimate(); } void PageWidgetDelegate::UpdateLifecycle( diff --git a/chromium/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc b/chromium/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc index caf3e53f6a5..a0489a1906b 100644 --- a/chromium/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc +++ b/chromium/third_party/blink/renderer/core/page/scrolling/fragment_anchor.cc @@ -32,14 +32,15 @@ FragmentAnchor* FragmentAnchor::TryCreate(const KURL& url, } // Count how often we see a # in the fragment (i.e. after the # delimiting - // the hash). We do this after trying to find a ##targetText so that we don't - // pollute this metric with our own feature since we're trying to determine - // how prevalent ## is. If a ##targetText is found, it'll strip off the ## - // and any text following it. + // the hash). We avoid counting cases with ##targetText since we're trying to + // determine how often this happens outside our feature so we don't want to + // pollute it with our own usage. if (url.HasFragmentIdentifier()) { size_t hash_pos = url.FragmentIdentifier().Find("#"); - if (hash_pos != kNotFound) - UseCounter::Count(frame.GetDocument(), WebFeature::kFragmentDoubleHash); + if (hash_pos != kNotFound) { + if (url.FragmentIdentifier().Find("#targetText=") == kNotFound) + UseCounter::Count(frame.GetDocument(), WebFeature::kFragmentDoubleHash); + } } bool element_id_anchor_found = false; diff --git a/chromium/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics_test.cc b/chromium/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics_test.cc index 03cfaf8333a..a378d56b09f 100644 --- a/chromium/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics_test.cc +++ b/chromium/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics_test.cc @@ -356,11 +356,6 @@ TEST_P(TextFragmentRelatedMetricTest, DoubleHashUseCounter) { const int kUncounted = 0; const int kCounted = 1; - // When the TextFragmentAnchors feature is on, we should avoid counting a - // ##targetText directive as a use of double-hash in this case. When it's - // off, we expect to count it. - const int kCountedOnlyIfDisabled = !GetParam() ? kCounted : kUncounted; - Vector<std::pair<String, int>> test_cases = { {"", kUncounted}, {"#element", kUncounted}, @@ -371,10 +366,14 @@ TEST_P(TextFragmentRelatedMetricTest, DoubleHashUseCounter) { {"#element#", kCounted}, {"#foo#bar#", kCounted}, {"#foo%23", kUncounted}, - {"#element##targetText=doesntexist", kCountedOnlyIfDisabled}, - {"#element##targetText=doesntexist#", kCountedOnlyIfDisabled}, - {"#element##targetText=page", kCountedOnlyIfDisabled}, - {"#element##targetText=page#", kCountedOnlyIfDisabled}, + {"#element##targetText=doesntexist", kUncounted}, + {"#element##targetText=doesntexist#", kUncounted}, + {"#element##targetText=page", kUncounted}, + {"#element##targetText=page#", kUncounted}, + {"##targetText=doesntexist", kUncounted}, + {"##targetText=doesntexist#", kUncounted}, + {"##targetText=page", kUncounted}, + {"##targetText=page#", kUncounted}, {"#targetText=doesntexist", kUncounted}, {"#targetText=page", kUncounted}}; diff --git a/chromium/third_party/blink/renderer/core/page/validation_message_client.h b/chromium/third_party/blink/renderer/core/page/validation_message_client.h index 09425262da3..8e9e1a248b4 100644 --- a/chromium/third_party/blink/renderer/core/page/validation_message_client.h +++ b/chromium/third_party/blink/renderer/core/page/validation_message_client.h @@ -62,6 +62,7 @@ class ValidationMessageClient : public GarbageCollectedMixin { virtual void WillBeDestroyed() = 0; + virtual void ServiceScriptedAnimations(base::TimeTicks) {} virtual void LayoutOverlay() {} virtual void UpdatePrePaint() {} // For CompositeAfterPaint. diff --git a/chromium/third_party/blink/renderer/core/page/validation_message_client_impl.cc b/chromium/third_party/blink/renderer/core/page/validation_message_client_impl.cc index 7ab6b3b84f1..b9142462dd6 100644 --- a/chromium/third_party/blink/renderer/core/page/validation_message_client_impl.cc +++ b/chromium/third_party/blink/renderer/core/page/validation_message_client_impl.cc @@ -195,6 +195,12 @@ void ValidationMessageClientImpl::WillOpenPopup() { HideValidationMessage(*current_anchor_); } +void ValidationMessageClientImpl::ServiceScriptedAnimations( + base::TimeTicks monotonic_frame_begin_time) { + if (overlay_) + overlay_->ServiceScriptedAnimations(monotonic_frame_begin_time); +} + void ValidationMessageClientImpl::LayoutOverlay() { if (overlay_) CheckAnchorStatus(nullptr); diff --git a/chromium/third_party/blink/renderer/core/page/validation_message_client_impl.h b/chromium/third_party/blink/renderer/core/page/validation_message_client_impl.h index ead9a0db738..4e9cac17e36 100644 --- a/chromium/third_party/blink/renderer/core/page/validation_message_client_impl.h +++ b/chromium/third_party/blink/renderer/core/page/validation_message_client_impl.h @@ -26,6 +26,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_VALIDATION_MESSAGE_CLIENT_IMPL_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_VALIDATION_MESSAGE_CLIENT_IMPL_H_ +#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/page/popup_opening_observer.h" #include "third_party/blink/renderer/core/page/validation_message_client.h" @@ -40,7 +41,7 @@ class LocalFrameView; class FrameOverlay; class ValidationMessageOverlayDelegate; -class ValidationMessageClientImpl final +class CORE_EXPORT ValidationMessageClientImpl final : public GarbageCollectedFinalized<ValidationMessageClientImpl>, public ValidationMessageClient, private PopupOpeningObserver { @@ -50,8 +51,18 @@ class ValidationMessageClientImpl final explicit ValidationMessageClientImpl(Page&); ~ValidationMessageClientImpl() override; + void ShowValidationMessage(const Element& anchor, + const String& message, + TextDirection message_dir, + const String& sub_message, + TextDirection sub_message_dir) override; + void Trace(blink::Visitor*) override; + ValidationMessageOverlayDelegate* GetDelegateForTesting() const { + return overlay_delegate_; + } + private: void CheckAnchorStatus(TimerBase*); LocalFrameView* CurrentView(); @@ -59,16 +70,12 @@ class ValidationMessageClientImpl final void Reset(TimerBase*); void ValidationMessageVisibilityChanged(const Element& anchor); - void ShowValidationMessage(const Element& anchor, - const String& message, - TextDirection message_dir, - const String& sub_message, - TextDirection sub_message_dir) override; void HideValidationMessage(const Element& anchor) override; bool IsValidationMessageVisible(const Element& anchor) override; void DocumentDetached(const Document&) override; void DidChangeFocusTo(const Element* new_element) override; void WillBeDestroyed() override; + void ServiceScriptedAnimations(base::TimeTicks) override; void LayoutOverlay() override; void UpdatePrePaint() override; void PaintOverlay(GraphicsContext&) override; diff --git a/chromium/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc b/chromium/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc index 8f738806995..604c73873d7 100644 --- a/chromium/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc +++ b/chromium/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc @@ -112,6 +112,11 @@ void ValidationMessageOverlayDelegate::PaintFrameOverlay( } } +void ValidationMessageOverlayDelegate::ServiceScriptedAnimations( + base::TimeTicks monotonic_frame_begin_time) { + page_->Animator().ServiceScriptedAnimations(monotonic_frame_begin_time); +} + void ValidationMessageOverlayDelegate::UpdateFrameViewState( const FrameOverlay& overlay, const IntSize& view_size) { diff --git a/chromium/third_party/blink/renderer/core/page/validation_message_overlay_delegate.h b/chromium/third_party/blink/renderer/core/page/validation_message_overlay_delegate.h index c48cfc16dd6..e3dfbb49c89 100644 --- a/chromium/third_party/blink/renderer/core/page/validation_message_overlay_delegate.h +++ b/chromium/third_party/blink/renderer/core/page/validation_message_overlay_delegate.h @@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_VALIDATION_MESSAGE_OVERLAY_DELEGATE_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_VALIDATION_MESSAGE_OVERLAY_DELEGATE_H_ +#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/frame/frame_overlay.h" #include "third_party/blink/renderer/platform/text/text_direction.h" #include "third_party/blink/renderer/platform/wtf/forward.h" @@ -23,7 +24,8 @@ class Page; // bubble is shown, and deleted when the bubble is closed. // // Ownership: A FrameOverlay instance owns a ValidationMessageOverlayDelegate. -class ValidationMessageOverlayDelegate : public FrameOverlay::Delegate { +class CORE_EXPORT ValidationMessageOverlayDelegate + : public FrameOverlay::Delegate { public: ValidationMessageOverlayDelegate(Page& main_page, const Element& anchor, @@ -34,12 +36,18 @@ class ValidationMessageOverlayDelegate : public FrameOverlay::Delegate { ~ValidationMessageOverlayDelegate() override; void CreatePage(const FrameOverlay&); + + // FrameOverlay::Delegate implementation. void PaintFrameOverlay(const FrameOverlay&, GraphicsContext&, const IntSize& view_size) const override; + void ServiceScriptedAnimations(base::TimeTicks) override; + void StartToHide(); bool IsHiding() const; + Page* GetPageForTesting() const { return page_; } + private: LocalFrameView& FrameView() const; void UpdateFrameViewState(const FrameOverlay&, const IntSize& view_size); diff --git a/chromium/third_party/blink/renderer/core/page/validation_message_overlay_delegate_test.cc b/chromium/third_party/blink/renderer/core/page/validation_message_overlay_delegate_test.cc new file mode 100644 index 00000000000..0a4a5a3b699 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/page/validation_message_overlay_delegate_test.cc @@ -0,0 +1,139 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/page/validation_message_overlay_delegate.h" + +#include "build/build_config.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/animation/animation.h" +#include "third_party/blink/renderer/core/animation/document_timeline.h" +#include "third_party/blink/renderer/core/page/page_widget_delegate.h" +#include "third_party/blink/renderer/core/page/validation_message_client.h" +#include "third_party/blink/renderer/core/page/validation_message_client_impl.h" +#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" +#include "third_party/blink/renderer/platform/testing/paint_test_configurations.h" +#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" +#include "third_party/blink/renderer/platform/web_test_support.h" + +#if defined(OS_WIN) +#include "base/strings/utf_string_conversions.h" +#include "third_party/blink/public/web/win/web_font_rendering.h" +#endif + +namespace blink { + +class ValidationMessageOverlayDelegateTest : public PaintTestConfigurations, + public RenderingTest { +#if defined(OS_WIN) + public: + void SetUp() override { + RenderingTest::SetUp(); + + // These tests appear to trigger a requirement for system fonts. On windows, + // an extra step is required to ensure that the system font is configured. + // See https://crbug.com/969622 + blink::WebFontRendering::SetMenuFontMetrics( + base::ASCIIToUTF16("Arial").c_str(), 12); + } +#endif +}; + +INSTANTIATE_PAINT_TEST_SUITE_P(ValidationMessageOverlayDelegateTest); + +// Regression test for https://crbug.com/990680, where we accidentally +// composited the animations created by ValidationMessageOverlayDelegate. Since +// overlays operate in a Page that has no compositor, the animations broke. +TEST_P(ValidationMessageOverlayDelegateTest, + OverlayAnimationsShouldNotBeComposited) { + // When WebTestSupport::IsRunningWebTest is set, the animations in + // ValidationMessageOverlayDelegate are disabled. We are specifically testing + // animations, so make sure that doesn't happen. + bool was_running_web_test = WebTestSupport::IsRunningWebTest(); + WebTestSupport::SetIsRunningWebTest(false); + + SetBodyInnerHTML("<div id='anchor'></div>"); + Element* anchor = GetElementById("anchor"); + ASSERT_TRUE(anchor); + + auto delegate = std::make_unique<ValidationMessageOverlayDelegate>( + GetPage(), *anchor, "Test message", TextDirection::kLtr, "Sub-message", + TextDirection::kLtr); + ValidationMessageOverlayDelegate* delegate_ptr = delegate.get(); + + auto overlay = + std::make_unique<FrameOverlay>(&GetFrame(), std::move(delegate)); + delegate_ptr->CreatePage(*overlay); + ASSERT_TRUE( + GetFrame().View()->UpdateLifecycleToCompositingCleanPlusScrolling()); + + // Trigger the overlay animations. + auto paint_controller = + std::make_unique<PaintController>(PaintController::kTransient); + GraphicsContext context(*paint_controller); + overlay->Paint(context); + + // Now find the related animations, and make sure they weren't composited. + Document* internal_document = + To<LocalFrame>(delegate_ptr->GetPageForTesting()->MainFrame()) + ->GetDocument(); + HeapVector<Member<Animation>> animations = + internal_document->Timeline().getAnimations(); + ASSERT_FALSE(animations.IsEmpty()); + + for (const auto& animation : animations) { + EXPECT_FALSE(animation->HasActiveAnimationsOnCompositor()); + } + + WebTestSupport::SetIsRunningWebTest(was_running_web_test); +} + +// Regression test for https://crbug.com/990680, where we found we were not +// properly advancing the AnimationClock in the internal Page created by +// ValidationMessageOverlayDelegate. When combined with the fix for +// https://crbug.com/785940, this caused Animations to never be updated. +TEST_P(ValidationMessageOverlayDelegateTest, + DelegatesInternalPageShouldHaveAnimationTimesUpdated) { + // We use a ValidationMessageClientImpl here to create our delegate since we + // need the official path from PageWidgetDelegate::Animate to work. + auto* client = MakeGarbageCollected<ValidationMessageClientImpl>(GetPage()); + ValidationMessageClient* original_client = + &GetPage().GetValidationMessageClient(); + GetPage().SetValidationMessageClientForTesting(client); + + SetBodyInnerHTML(R"HTML( + <style>#anchor { width: 100px; height: 100px; }</style> + <div id='anchor'></div> + )HTML"); + Element* anchor = GetElementById("anchor"); + ASSERT_TRUE(anchor); + + client->ShowValidationMessage(*anchor, "Test message", TextDirection::kLtr, + "Sub-message", TextDirection::kLtr); + ValidationMessageOverlayDelegate* delegate = client->GetDelegateForTesting(); + ASSERT_TRUE(delegate); + + // Initially the AnimationClock will be at 0. + // TODO(crbug.com/785940): Re-enable this EXPECT_EQ once the AnimationClock no + // longer jumps ahead on its own accord. + AnimationClock& internal_clock = + delegate->GetPageForTesting()->Animator().Clock(); + // EXPECT_EQ(internal_clock.CurrentTime(), 0); + + // Now update the main Page's clock. This should trickle down and update the + // inner Page's clock too. + AnimationClock& external_clock = GetPage().Animator().Clock(); + base::TimeTicks current_time = external_clock.CurrentTime(); + + base::TimeTicks new_time = current_time + base::TimeDelta::FromSeconds(1); + PageWidgetDelegate::Animate(GetPage(), new_time); + + // TODO(crbug.com/785940): Until this bug is fixed, this comparison could pass + // even if the underlying behavior regresses (because calling CurrentTime + // could advance the clocks anyway). + EXPECT_EQ(external_clock.CurrentTime(), internal_clock.CurrentTime()); + + GetPage().SetValidationMessageClientForTesting(original_client); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/devtools/front_end/audits/auditsPanel.css b/chromium/third_party/blink/renderer/devtools/front_end/audits/auditsPanel.css index bba66cffaa9..4a1819cc917 100644 --- a/chromium/third_party/blink/renderer/devtools/front_end/audits/auditsPanel.css +++ b/chromium/third_party/blink/renderer/devtools/front_end/audits/auditsPanel.css @@ -28,6 +28,6 @@ button.view-trace { /** `window.opener` is null for windows opened from DevTools. This breaks the LH viewer app, so disable this feature. */ -.lh-export--viewer { +.lh-tools--viewer { display: none !important; } diff --git a/chromium/third_party/blink/renderer/devtools/front_end/text_editor/cmdevtools.css b/chromium/third_party/blink/renderer/devtools/front_end/text_editor/cmdevtools.css index 7b5253ab56e..92be9fe9c0e 100644 --- a/chromium/third_party/blink/renderer/devtools/front_end/text_editor/cmdevtools.css +++ b/chromium/third_party/blink/renderer/devtools/front_end/text_editor/cmdevtools.css @@ -441,7 +441,7 @@ div.CodeMirror:focus-within span.CodeMirror-nonmatchingbracket { } .CodeMirror .text-editor-coverage-unused-marker::after { - content: " "; + content: "\200B"; } .CodeMirror .text-editor-coverage-used-marker { @@ -451,7 +451,7 @@ div.CodeMirror:focus-within span.CodeMirror-nonmatchingbracket { } .CodeMirror .text-editor-coverage-used-marker::after { - content: " "; + content: "\200B"; } .CodeMirror .text-editor-line-decoration { diff --git a/chromium/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/chromium/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc index d3ca692a2fa..5eaeb7e4cc2 100644 --- a/chromium/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc +++ b/chromium/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc @@ -607,12 +607,25 @@ bool AXLayoutObject::ComputeAccessibilityIsIgnored( DCHECK(initialized_); #endif + // All nodes must have an unignored parent within their tree under + // kRootWebArea, so force kRootWebArea to always be unignored. + if (role_ == ax::mojom::Role::kRootWebArea) + return false; + if (!layout_object_) { if (ignored_reasons) ignored_reasons->push_back(IgnoredReason(kAXNotRendered)); return true; } + // Ignore continuations, since those are essentially duplicate copies + // of inline nodes with blocks inside. + if (layout_object_->IsElementContinuation()) { + if (ignored_reasons) + ignored_reasons->push_back(IgnoredReason(kAXUninteresting)); + return true; + } + // Check first if any of the common reasons cause this element to be ignored. AXObjectInclusion default_inclusion = DefaultObjectInclusion(ignored_reasons); if (default_inclusion == kIncludeObject) @@ -633,14 +646,6 @@ bool AXLayoutObject::ComputeAccessibilityIsIgnored( return true; } - // Ignore continuations, since those are essentially duplicate copies - // of inline nodes with blocks inside. - if (layout_object_->IsElementContinuation()) { - if (ignored_reasons) - ignored_reasons->push_back(IgnoredReason(kAXUninteresting)); - return true; - } - // A LayoutEmbeddedContent is an iframe element or embedded object element or // something like that. We don't want to ignore those. if (layout_object_->IsLayoutEmbeddedContent()) @@ -2045,8 +2050,12 @@ void AXLayoutObject::AddChildren() { if (IsDetached()) return; + // Avoid calling AXNodeObject logic for continuations. + bool is_continuation = layout_object_->IsElementContinuation(); + if (auto* element = DynamicTo<Element>(GetNode())) { - if (!IsHTMLMapElement(*element) && // Handled in AddImageMapChildren (img) + if (!is_continuation && + !IsHTMLMapElement(*element) && // Handled in AddImageMapChildren (img) !IsHTMLRubyElement(*element) && // Special layout handling !IsHTMLTableElement(*element) && // thead/tfoot move around !element->IsPseudoElement()) { // Not visited in layout traversal @@ -2065,10 +2074,8 @@ void AXLayoutObject::AddChildren() { ComputeAriaOwnsChildren(owned_children); for (AXObject* obj = RawFirstChild(); obj; obj = obj->RawNextSibling()) { - if (!AXObjectCache().IsAriaOwned(obj)) { - obj->SetParent(this); + if (!AXObjectCache().IsAriaOwned(obj)) AddChild(obj); - } } AddHiddenChildren(); @@ -2080,8 +2087,11 @@ void AXLayoutObject::AddChildren() { AddAccessibleNodeChildren(); for (const auto& child : children_) { - if (!child->CachedParentObject()) + if (!is_continuation && !child->CachedParentObject()) { + // Never set continuations as a parent object. The first layout object + // in the chain must be used instead. child->SetParent(this); + } } for (const auto& owned_child : owned_children) diff --git a/chromium/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/chromium/third_party/blink/renderer/modules/accessibility/ax_node_object.cc index 5c76a39946c..e3d7132ddd8 100644 --- a/chromium/third_party/blink/renderer/modules/accessibility/ax_node_object.cc +++ b/chromium/third_party/blink/renderer/modules/accessibility/ax_node_object.cc @@ -332,6 +332,12 @@ bool AXNodeObject::ComputeAccessibilityIsIgnored( // it's been initialized. DCHECK(initialized_); #endif + + // All nodes must have an unignored parent within their tree under + // kRootWebArea, so force kRootWebArea to always be unignored. + if (role_ == ax::mojom::Role::kRootWebArea) + return false; + if (GetLayoutObject()) { if (role_ == ax::mojom::Role::kUnknown) { if (ignored_reasons) diff --git a/chromium/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc b/chromium/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc index 9b6f96373c8..c84797f978b 100644 --- a/chromium/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc +++ b/chromium/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc @@ -442,8 +442,8 @@ VideoTrackRecorder::VideoTrackRecorder( DCHECK(track_->Source()->GetType() == MediaStreamSource::kTypeVideo); initialize_encoder_callback_ = WTF::BindRepeating( - &VideoTrackRecorder::InitializeEncoder, weak_ptr_factory_.GetWeakPtr(), - codec, on_encoded_video_callback, bits_per_second); + &VideoTrackRecorder::InitializeEncoder, WrapWeakPersistent(this), codec, + on_encoded_video_callback, bits_per_second); // InitializeEncoder() will be called on Render Main thread. ConnectToTrack(media::BindToCurrentLoop(WTF::BindRepeating( @@ -508,7 +508,7 @@ void VideoTrackRecorder::InitializeEncoder( encoder_ = VEAEncoder::Create( on_encoded_video_callback, media::BindToCurrentLoop(WTF::BindRepeating( - &VideoTrackRecorder::OnError, weak_ptr_factory_.GetWeakPtr())), + &VideoTrackRecorder::OnError, WrapWeakPersistent(this))), bits_per_second, vea_profile, input_size, main_task_runner_); } else { UMA_HISTOGRAM_BOOLEAN("Media.MediaRecorder.VEAUsed", false); diff --git a/chromium/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h b/chromium/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h index ae8c208b615..f9d37271fdc 100644 --- a/chromium/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h +++ b/chromium/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h @@ -317,8 +317,6 @@ class MODULES_EXPORT VideoTrackRecorder scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; - base::WeakPtrFactory<VideoTrackRecorder> weak_ptr_factory_{this}; - DISALLOW_COPY_AND_ASSIGN(VideoTrackRecorder); }; diff --git a/chromium/third_party/blink/renderer/platform/graphics/color_behavior.h b/chromium/third_party/blink/renderer/platform/graphics/color_behavior.h index ce04f84a5c5..7d3c6ab9616 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/color_behavior.h +++ b/chromium/third_party/blink/renderer/platform/graphics/color_behavior.h @@ -7,6 +7,7 @@ #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h" #include "ui/gfx/color_space.h" namespace blink { @@ -49,4 +50,15 @@ class PLATFORM_EXPORT ColorBehavior { } // namespace blink +namespace WTF { + +template <> +struct CrossThreadCopier<blink::ColorBehavior> { + STATIC_ONLY(CrossThreadCopier); + using Type = blink::ColorBehavior; + static Type Copy(Type pointer) { return pointer; } +}; + +} // namespace WTF + #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_BEHAVIOR_H_ diff --git a/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn b/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn index 3775eab8b07..e4fac40131b 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn +++ b/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn @@ -292,6 +292,7 @@ jumbo_source_set("wtf_unittests_sources") { "allocator/partitions_test.cc", "ascii_ctype_test.cc", "assertions_test.cc", + "cross_thread_functional_test.cc", "decimal_test.cc", "deque_test.cc", "doubly_linked_list_test.cc", diff --git a/chromium/third_party/blink/renderer/platform/wtf/cross_thread_functional.h b/chromium/third_party/blink/renderer/platform/wtf/cross_thread_functional.h index 2940e661189..92706732025 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/cross_thread_functional.h +++ b/chromium/third_party/blink/renderer/platform/wtf/cross_thread_functional.h @@ -33,30 +33,65 @@ namespace WTF { // Bind(&Func1, 42, str); // Bind(&Func1, 42, str.IsolatedCopy()); +namespace internal { + +// Deduction of the signature to avoid complicated calls to MakeUnboundRunType. + +template <typename Signature> +auto MakeCrossThreadFunction(base::RepeatingCallback<Signature> callback) { + return CrossThreadFunction<Signature>(std::move(callback)); +} + +template <typename Signature> +auto MakeCrossThreadOnceFunction(base::OnceCallback<Signature> callback) { + return CrossThreadOnceFunction<Signature>(std::move(callback)); +} + +// Insertion of coercion for specific types; transparent forwarding otherwise. + +template <typename T> +decltype(auto) CoerceFunctorForCrossThreadBind(T&& functor) { + return std::forward<T>(functor); +} + +template <typename Signature> +base::RepeatingCallback<Signature> CoerceFunctorForCrossThreadBind( + CrossThreadFunction<Signature>&& functor) { + return ConvertToBaseCallback(std::move(functor)); +} + +template <typename Signature> +base::OnceCallback<Signature> CoerceFunctorForCrossThreadBind( + CrossThreadOnceFunction<Signature>&& functor) { + return ConvertToBaseOnceCallback(std::move(functor)); +} + +} // namespace internal + template <typename FunctionType, typename... Ps> -CrossThreadFunction<base::MakeUnboundRunType<FunctionType, Ps...>> -CrossThreadBindRepeating(FunctionType&& function, Ps&&... parameters) { +auto CrossThreadBindRepeating(FunctionType&& function, Ps&&... parameters) { static_assert( internal::CheckGCedTypeRestrictions<std::index_sequence_for<Ps...>, std::decay_t<Ps>...>::ok, "A bound argument uses a bad pattern."); - using UnboundRunType = base::MakeUnboundRunType<FunctionType, Ps...>; - return CrossThreadFunction<UnboundRunType>( - base::Bind(function, CrossThreadCopier<std::decay_t<Ps>>::Copy( - std::forward<Ps>(parameters))...)); + return internal::MakeCrossThreadFunction( + base::Bind(internal::CoerceFunctorForCrossThreadBind( + std::forward<FunctionType>(function)), + CrossThreadCopier<std::decay_t<Ps>>::Copy( + std::forward<Ps>(parameters))...)); } template <typename FunctionType, typename... Ps> -CrossThreadOnceFunction<base::MakeUnboundRunType<FunctionType, Ps...>> -CrossThreadBindOnce(FunctionType&& function, Ps&&... parameters) { +auto CrossThreadBindOnce(FunctionType&& function, Ps&&... parameters) { static_assert( internal::CheckGCedTypeRestrictions<std::index_sequence_for<Ps...>, std::decay_t<Ps>...>::ok, "A bound argument uses a bad pattern."); - using UnboundRunType = base::MakeUnboundRunType<FunctionType, Ps...>; - return CrossThreadOnceFunction<UnboundRunType>(base::BindOnce( - std::move(function), CrossThreadCopier<std::decay_t<Ps>>::Copy( - std::forward<Ps>(parameters))...)); + return internal::MakeCrossThreadOnceFunction( + base::BindOnce(internal::CoerceFunctorForCrossThreadBind( + std::forward<FunctionType>(function)), + CrossThreadCopier<std::decay_t<Ps>>::Copy( + std::forward<Ps>(parameters))...)); } } // namespace WTF diff --git a/chromium/third_party/blink/renderer/platform/wtf/cross_thread_functional_test.cc b/chromium/third_party/blink/renderer/platform/wtf/cross_thread_functional_test.cc new file mode 100644 index 00000000000..35639f18423 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/wtf/cross_thread_functional_test.cc @@ -0,0 +1,46 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" + +#include <utility> +#include "testing/gtest/include/gtest/gtest.h" + +namespace WTF { +namespace { + +// Tests that "currying" CrossThreadFunction and CrossThreadOnceFunction works, +// as it does with the base counterparts. + +struct SomeFunctor; + +static_assert(std::is_same<decltype(internal::CoerceFunctorForCrossThreadBind( + std::declval<SomeFunctor&>())), + SomeFunctor&>(), + "functor coercion should not affect Functor lvalue ref type"); +static_assert(std::is_same<decltype(internal::CoerceFunctorForCrossThreadBind( + std::declval<SomeFunctor>())), + SomeFunctor&&>(), + "functor coercion should not affect Functor rvalue ref type"); + +TEST(CrossThreadFunctionalTest, CrossThreadBindRepeating_CrossThreadFunction) { + auto adder = CrossThreadBindRepeating([](int x, int y) { return x + y; }); + auto five_adder = CrossThreadBindRepeating(std::move(adder), 5); + EXPECT_EQ(five_adder.Run(7), 12); +} + +TEST(CrossThreadFunctionalTest, CrossThreadBindOnce_CrossThreadOnceFunction) { + auto adder = CrossThreadBindOnce([](int x, int y) { return x + y; }); + auto five_adder = CrossThreadBindOnce(std::move(adder), 5); + EXPECT_EQ(std::move(five_adder).Run(7), 12); +} + +TEST(CrossThreadFunctionalTest, CrossThreadBindOnce_CrossThreadFunction) { + auto adder = CrossThreadBindRepeating([](int x, int y) { return x + y; }); + auto five_adder = CrossThreadBindOnce(std::move(adder), 5); + EXPECT_EQ(std::move(five_adder).Run(7), 12); +} + +} // namespace +} // namespace WTF diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h index 601046eca67..14d9943c352 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h +++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h @@ -32,6 +32,7 @@ #include "base/memory/scoped_refptr.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h" #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h" #include "third_party/blink/renderer/platform/wtf/wtf.h" #include "third_party/blink/renderer/platform/wtf/wtf_export.h" @@ -56,6 +57,8 @@ class WTF_EXPORT ArrayBufferContents { DISALLOW_COPY_AND_ASSIGN(DataHandle); public: + DataHandle() : DataHandle(nullptr, 0, nullptr, nullptr) {} + DataHandle(void* data, size_t length, DataDeleter deleter, @@ -82,6 +85,8 @@ class WTF_EXPORT ArrayBufferContents { return *this; } + void reset() { *this = DataHandle(); } + void* Data() const { return data_; } size_t DataLength() const { return data_length_; } @@ -226,6 +231,15 @@ class WTF_EXPORT ArrayBufferContents { DISALLOW_COPY_AND_ASSIGN(ArrayBufferContents); }; +template <> +struct CrossThreadCopier<ArrayBufferContents::DataHandle> { + STATIC_ONLY(CrossThreadCopier); + using Type = ArrayBufferContents::DataHandle; + static Type Copy(Type handle) { + return handle; // This is in fact a move. + } +}; + } // namespace WTF #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_BUFFER_CONTENTS_H_ diff --git a/chromium/third_party/skia/src/pdf/SkPDFFont.cpp b/chromium/third_party/skia/src/pdf/SkPDFFont.cpp index 33ec05b76ab..ba79a15c9a5 100644 --- a/chromium/third_party/skia/src/pdf/SkPDFFont.cpp +++ b/chromium/third_party/skia/src/pdf/SkPDFFont.cpp @@ -231,7 +231,11 @@ SkPDFFont::SkPDFFont(sk_sp<SkTypeface> typeface, : fTypeface(std::move(typeface)) , fGlyphUsage(firstGlyphID, lastGlyphID) , fIndirectReference(indirectReference) - , fFontType(fontType) {} + , fFontType(fontType) +{ + // Always include glyph 0 + this->noteGlyphUsage(0); +} void SkPDFFont::PopulateCommonFontDescriptor(SkPDFDict* descriptor, const SkAdvancedTypefaceMetrics& metrics, @@ -290,7 +294,7 @@ static void emit_subset_type0(const SkPDFFont& font, SkPDFDocument* doc) { auto descriptor = SkPDFMakeDict("FontDescriptor"); uint16_t emSize = SkToU16(font.typeface()->getUnitsPerEm()); - SkPDFFont::PopulateCommonFontDescriptor(descriptor.get(), metrics, emSize , 0); + SkPDFFont::PopulateCommonFontDescriptor(descriptor.get(), metrics, emSize, 0); int ttcIndex; std::unique_ptr<SkStreamAsset> fontAsset = face->openStream(&ttcIndex); @@ -369,7 +373,7 @@ static void emit_subset_type0(const SkPDFFont& font, SkPDFDocument* doc) { SkScalar defaultWidth = 0; { std::unique_ptr<SkPDFArray> widths = SkPDFMakeCIDGlyphWidthsArray( - *face, &font.glyphUsage(), &defaultWidth); + *face, font.glyphUsage(), &defaultWidth); if (widths && widths->size() > 0) { newCIDFont->insertObject("W", std::move(widths)); } diff --git a/chromium/third_party/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp b/chromium/third_party/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp index 589cfa76202..210202db4d1 100644 --- a/chromium/third_party/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp +++ b/chromium/third_party/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp @@ -13,6 +13,7 @@ #include "src/core/SkStrikeSpec.h" #include "src/pdf/SkPDFGlyphUse.h" +#include <algorithm> #include <vector> // TODO(halcanary): Write unit tests for SkPDFMakeCIDGlyphWidthsArray(). @@ -23,28 +24,8 @@ namespace { -struct AdvanceMetric { - enum MetricType { - kDefault, // Default advance: fAdvance.count = 1 - kRange, // Advances for a range: fAdvance.count = fEndID-fStartID - kRun // fStartID-fEndID have same advance: fAdvance.count = 1 - }; - MetricType fType; - uint16_t fStartId; - uint16_t fEndId; - std::vector<int16_t> fAdvance; - AdvanceMetric(uint16_t startId) : fStartId(startId) {} - AdvanceMetric(AdvanceMetric&&) = default; - AdvanceMetric& operator=(AdvanceMetric&& other) = default; - AdvanceMetric(const AdvanceMetric&) = delete; - AdvanceMetric& operator=(const AdvanceMetric&) = delete; -}; -const int16_t kInvalidAdvance = SK_MinS16; -const int16_t kDontCareAdvance = SK_MinS16 + 1; -} // namespace - // scale from em-units to base-1000, returning as a SkScalar -static SkScalar from_font_units(SkScalar scaled, uint16_t emSize) { +SkScalar from_font_units(SkScalar scaled, uint16_t emSize) { if (emSize == 1000) { return scaled; } else { @@ -52,224 +33,174 @@ static SkScalar from_font_units(SkScalar scaled, uint16_t emSize) { } } -static SkScalar scale_from_font_units(int16_t val, uint16_t emSize) { +SkScalar scale_from_font_units(int16_t val, uint16_t emSize) { return from_font_units(SkIntToScalar(val), emSize); } -static void strip_uninteresting_trailing_advances_from_range( - AdvanceMetric* range) { - SkASSERT(range); - - int expectedAdvanceCount = range->fEndId - range->fStartId + 1; - if (SkToInt(range->fAdvance.size()) < expectedAdvanceCount) { - return; - } - - for (int i = expectedAdvanceCount - 1; i >= 0; --i) { - if (range->fAdvance[i] != kDontCareAdvance && - range->fAdvance[i] != kInvalidAdvance && - range->fAdvance[i] != 0) { - range->fEndId = range->fStartId + i; - break; - } - } -} - -static void zero_wildcards_in_range(AdvanceMetric* range) { - SkASSERT(range); - if (range->fType != AdvanceMetric::kRange) { - return; +// Unfortunately poppler does not appear to respect the default width setting. +#if defined(SK_PDF_CAN_USE_DW) +int16_t findMode(SkSpan<const int16_t> advances) { + if (advances.empty()) { + return 0; } - SkASSERT(SkToInt(range->fAdvance.size()) == range->fEndId - range->fStartId + 1); - // Zero out wildcards. - for (size_t i = 0; i < range->fAdvance.size(); ++i) { - if (range->fAdvance[i] == kDontCareAdvance) { - range->fAdvance[i] = 0; - } - } -} + int16_t previousAdvance = advances[0]; + int16_t currentModeAdvance = advances[0]; + size_t currentCount = 1; + size_t currentModeCount = 1; -static void finish_range( - AdvanceMetric* range, - int endId, - AdvanceMetric::MetricType type) { - range->fEndId = endId; - range->fType = type; - strip_uninteresting_trailing_advances_from_range(range); - size_t newLength; - if (type == AdvanceMetric::kRange) { - newLength = range->fEndId - range->fStartId + 1; - } else { - if (range->fEndId == range->fStartId) { - range->fType = AdvanceMetric::kRange; + for (size_t i = 1; i < advances.size(); ++i) { + if (advances[i] == previousAdvance) { + ++currentCount; + } else { + if (currentCount > currentModeCount) { + currentModeAdvance = previousAdvance; + currentModeCount = currentCount; + } + previousAdvance = advances[i]; + currentCount = 1; } - newLength = 1; } - SkASSERT(range->fAdvance.size() >= newLength); - range->fAdvance.resize(newLength); - zero_wildcards_in_range(range); -} -static void compose_advance_data(const AdvanceMetric& range, - uint16_t emSize, - SkScalar* defaultAdvance, - SkPDFArray* result) { - switch (range.fType) { - case AdvanceMetric::kDefault: { - SkASSERT(range.fAdvance.size() == 1); - *defaultAdvance = scale_from_font_units(range.fAdvance[0], emSize); - break; - } - case AdvanceMetric::kRange: { - auto advanceArray = SkPDFMakeArray(); - for (size_t j = 0; j < range.fAdvance.size(); j++) - advanceArray->appendScalar(scale_from_font_units(range.fAdvance[j], emSize)); - result->appendInt(range.fStartId); - result->appendObject(std::move(advanceArray)); - break; - } - case AdvanceMetric::kRun: { - SkASSERT(range.fAdvance.size() == 1); - result->appendInt(range.fStartId); - result->appendInt(range.fEndId); - result->appendScalar(scale_from_font_units(range.fAdvance[0], emSize)); - break; - } - } + return currentCount > currentModeCount ? previousAdvance : currentModeAdvance; } +#endif +} // namespace /** Retrieve advance data for glyphs. Used by the PDF backend. */ // TODO(halcanary): this function is complex enough to need its logic // tested with unit tests. std::unique_ptr<SkPDFArray> SkPDFMakeCIDGlyphWidthsArray(const SkTypeface& typeface, - const SkPDFGlyphUse* subset, + const SkPDFGlyphUse& subset, SkScalar* defaultAdvance) { - // Assuming that on average, the ASCII representation of an advance plus - // a space is 8 characters and the ASCII representation of a glyph id is 3 - // characters, then the following cut offs for using different range types - // apply: - // The cost of stopping and starting the range is 7 characters - // a. Removing 4 0's or don't care's is a win - // The cost of stopping and starting the range plus a run is 22 - // characters - // b. Removing 3 repeating advances is a win - // c. Removing 2 repeating advances and 3 don't cares is a win - // When not currently in a range the cost of a run over a range is 16 - // characters, so: - // d. Removing a leading 0/don't cares is a win because it is omitted - // e. Removing 2 repeating advances is a win + // There are two ways of expressing advances + // + // range: " gfid [adv.ances adv.ances ... adv.ances]" + // run: " gfid gfid adv.ances" + // + // Assuming that on average + // the ASCII representation of an advance plus a space is 10 characters + // the ASCII representation of a glyph id plus a space is 4 characters + // the ASCII representation of unused gid plus a space in a range is 2 characters + // + // When not in a range or run + // a. Skipping don't cares or defaults is a win (trivial) + // b. Run wins for 2+ repeats " gid gid adv.ances" + // " gid [adv.ances adv.ances]" + // rule: 2+ repeats create run as long as possible, else start range + // + // When in a range + // Cost of stopping and starting a range is 8 characters "] gid [" + // c. Skipping defaults is always a win " adv.ances" + // rule: end range if default seen + // d. Skipping 4+ don't cares is a win " 0 0 0 0" + // rule: end range if 4+ don't cares + // Cost of stop and start range plus run is 28 characters "] gid gid adv.ances gid [" + // e. Switching for 2+ repeats and 4+ don't cares wins " 0 0 adv.ances 0 0 adv.ances" + // rule: end range for 2+ repeats with 4+ don't cares + // f. Switching for 3+ repeats wins " adv.ances adv.ances adv.ances" + // rule: end range for 3+ repeats int emSize; SkStrikeSpec strikeSpec = SkStrikeSpec::MakePDFVector(typeface, &emSize); SkBulkGlyphMetricsAndPaths paths{strikeSpec}; auto result = SkPDFMakeArray(); - int num_glyphs = SkToInt(typeface.countGlyphs()); - - bool prevRange = false; - - int16_t lastAdvance = kInvalidAdvance; - int repeatedAdvances = 0; - int wildCardsInRun = 0; - int trailingWildCards = 0; - // Limit the loop count to glyph id ranges provided. - int lastIndex = num_glyphs; - if (subset) { - while (!subset->has(lastIndex - 1) && lastIndex > 0) { - --lastIndex; - } - } - AdvanceMetric curRange(0); - - SkAutoTArray<SkGlyphID> glyphIDs{lastIndex + 1}; - for (int gId = 0; gId <= lastIndex; gId++) { - glyphIDs[gId] = gId; + std::vector<SkGlyphID> glyphIDs; + subset.getSetValues([&](unsigned index) { + glyphIDs.push_back(SkToU16(index)); + }); + auto glyphs = paths.glyphs(SkMakeSpan(glyphIDs)); + +#if defined(SK_PDF_CAN_USE_DW) + std::vector<int16_t> advances; + advances.reserve(glyphs.size()); + for (const SkGlyph* glyph : glyphs) { + advances.push_back((int16_t)glyph->advanceX()); } - - auto glyphs = paths.glyphs(SkMakeSpan(glyphIDs.get(), lastIndex + 1)); - - for (int gId = 0; gId <= lastIndex; gId++) { - int16_t advance = kInvalidAdvance; - if (gId < lastIndex) { - if (!subset || 0 == gId || subset->has(gId)) { - advance = (int16_t)glyphs[gId]->advanceX(); - } else { - advance = kDontCareAdvance; - } + std::sort(advances.begin(), advances.end()); + int16_t modeAdvance = findMode(SkMakeSpan(advances)); + *defaultAdvance = scale_from_font_units(modeAdvance, emSize); +#else + *defaultAdvance = 0; +#endif + + for (size_t i = 0; i < glyphs.size(); ++i) { + int16_t advance = (int16_t)glyphs[i]->advanceX(); + +#if defined(SK_PDF_CAN_USE_DW) + // a. Skipping don't cares or defaults is a win (trivial) + if (advance == modeAdvance) { + continue; } - if (advance == lastAdvance) { - repeatedAdvances++; - trailingWildCards = 0; - } else if (advance == kDontCareAdvance) { - wildCardsInRun++; - trailingWildCards++; - } else if (SkToInt(curRange.fAdvance.size()) == - repeatedAdvances + 1 + wildCardsInRun) { // All in run. - if (lastAdvance == 0) { - curRange.fStartId = gId; // reset - curRange.fAdvance.resize(0); - trailingWildCards = 0; - } else if (repeatedAdvances + 1 >= 2 || trailingWildCards >= 4) { - finish_range(&curRange, gId - 1, AdvanceMetric::kRun); - compose_advance_data(curRange, emSize, defaultAdvance, result.get()); - prevRange = true; - curRange = AdvanceMetric(gId); - trailingWildCards = 0; +#endif + + // b. 2+ repeats create run as long as possible, else start range + { + size_t j = i + 1; // j is always one past the last known repeat + for (; j < glyphs.size(); ++j) { + int16_t next_advance = (int16_t)glyphs[j]->advanceX(); + if (advance != next_advance) { + break; + } } - repeatedAdvances = 0; - wildCardsInRun = trailingWildCards; - trailingWildCards = 0; - } else { - if (lastAdvance == 0 && - repeatedAdvances + 1 + wildCardsInRun >= 4) { - finish_range(&curRange, - gId - repeatedAdvances - wildCardsInRun - 2, - AdvanceMetric::kRange); - compose_advance_data(curRange, emSize, defaultAdvance, result.get()); - prevRange = true; - curRange = AdvanceMetric(gId); - trailingWildCards = 0; - } else if (trailingWildCards >= 4 && repeatedAdvances + 1 < 2) { - finish_range(&curRange, gId - trailingWildCards - 1, - AdvanceMetric::kRange); - compose_advance_data(curRange, emSize, defaultAdvance, result.get()); - prevRange = true; - curRange = AdvanceMetric(gId); - trailingWildCards = 0; - } else if (lastAdvance != 0 && - (repeatedAdvances + 1 >= 3 || - (repeatedAdvances + 1 >= 2 && wildCardsInRun >= 3))) { - finish_range(&curRange, - gId - repeatedAdvances - wildCardsInRun - 2, - AdvanceMetric::kRange); - compose_advance_data(curRange, emSize, defaultAdvance, result.get()); - curRange = - AdvanceMetric(gId - repeatedAdvances - wildCardsInRun - 1); - curRange.fAdvance.push_back(lastAdvance); - finish_range(&curRange, gId - 1, AdvanceMetric::kRun); - compose_advance_data(curRange, emSize, defaultAdvance, result.get()); - prevRange = true; - curRange = AdvanceMetric(gId); - trailingWildCards = 0; + if (j - i >= 2) { + result->appendInt(glyphs[i]->getGlyphID()); + result->appendInt(glyphs[j - 1]->getGlyphID()); + result->appendScalar(scale_from_font_units(advance, emSize)); + i = j - 1; + continue; } - repeatedAdvances = 0; - wildCardsInRun = trailingWildCards; - trailingWildCards = 0; - } - curRange.fAdvance.push_back(advance); - if (advance != kDontCareAdvance) { - lastAdvance = advance; } - } - if (curRange.fStartId == lastIndex) { - if (!prevRange) { - return nullptr; // https://crbug.com/567031 + + { + result->appendInt(glyphs[i]->getGlyphID()); + auto advanceArray = SkPDFMakeArray(); + advanceArray->appendScalar(scale_from_font_units(advance, emSize)); + size_t j = i + 1; // j is always one past the last output + for (; j < glyphs.size(); ++j) { + advance = (int16_t)glyphs[j]->advanceX(); +#if defined(SK_PDF_CAN_USE_DW) + // c. end range if default seen + if (advance == modeAdvance) { + break; + } +#endif + + int dontCares = glyphs[j]->getGlyphID() - glyphs[j - 1]->getGlyphID() - 1; + // d. end range if 4+ don't cares + if (dontCares >= 4) { + break; + } + + int16_t next_advance = 0; + // e. end range for 2+ repeats with 4+ don't cares + if (j + 1 < glyphs.size()) { + next_advance = (int16_t)glyphs[j+1]->advanceX(); + int next_dontCares = glyphs[j+1]->getGlyphID() - glyphs[j]->getGlyphID() - 1; + if (advance == next_advance && dontCares + next_dontCares >= 4) { + break; + } + } + + // f. end range for 3+ repeats + if (j + 2 < glyphs.size() && advance == next_advance) { + next_advance = (int16_t)glyphs[j+2]->advanceX(); + if (advance == next_advance) { + break; + } + } + + while (dontCares --> 0) { + advanceArray->appendScalar(0); + } + advanceArray->appendScalar(scale_from_font_units(advance, emSize)); + } + result->appendObject(std::move(advanceArray)); + i = j - 1; } - } else { - finish_range(&curRange, lastIndex - 1, AdvanceMetric::kRange); - compose_advance_data(curRange, emSize, defaultAdvance, result.get()); } + return result; } diff --git a/chromium/third_party/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.h b/chromium/third_party/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.h index b2e1e1df55a..041fe28d8cb 100644 --- a/chromium/third_party/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.h +++ b/chromium/third_party/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.h @@ -17,7 +17,7 @@ class SkTypeface; format that can specify individual widths for consecutive CIDs or one width for a range of CIDs". */ std::unique_ptr<SkPDFArray> SkPDFMakeCIDGlyphWidthsArray(const SkTypeface& typeface, - const SkPDFGlyphUse* subset, + const SkPDFGlyphUse& subset, SkScalar* defaultAdvance); #endif // SkPDFMakeCIDGlyphWidthsArray_DEFINED diff --git a/chromium/third_party/skia/src/pdf/SkPDFSubsetFont.cpp b/chromium/third_party/skia/src/pdf/SkPDFSubsetFont.cpp index 0bb92bb9622..5485f3b4ba3 100644 --- a/chromium/third_party/skia/src/pdf/SkPDFSubsetFont.cpp +++ b/chromium/third_party/skia/src/pdf/SkPDFSubsetFont.cpp @@ -68,7 +68,6 @@ static sk_sp<SkData> subset_harfbuzz(sk_sp<SkData> fontData, return nullptr; } hb_set_t* glyphs = hb_subset_input_glyph_set(input.get()); - hb_set_add(glyphs, 0); glyphUsage.getSetValues([&glyphs](unsigned gid) { hb_set_add(glyphs, gid);}); hb_subset_input_set_retain_gids(input.get(), true); @@ -99,9 +98,6 @@ static sk_sp<SkData> subset_sfntly(sk_sp<SkData> fontData, // Generate glyph id array in format needed by sfntly. // TODO(halcanary): sfntly should take a more compact format. std::vector<unsigned> subset; - if (!glyphUsage.has(0)) { - subset.push_back(0); // Always include glyph 0. - } glyphUsage.getSetValues([&subset](unsigned v) { subset.push_back(v); }); unsigned char* subsetFont{nullptr}; diff --git a/chromium/tools/mb/mb.py b/chromium/tools/mb/mb.py index ba3af5aa1a7..d7ab48952ef 100755 --- a/chromium/tools/mb/mb.py +++ b/chromium/tools/mb/mb.py @@ -36,8 +36,31 @@ sys.path = [os.path.join(CHROMIUM_SRC_DIR, 'build')] + sys.path import gn_helpers +def PruneVirtualEnv(): + # Set by VirtualEnv, no need to keep it. + os.environ.pop('VIRTUAL_ENV', None) + + # Set by VPython, if scripts want it back they have to set it explicitly. + os.environ.pop('PYTHONNOUSERSITE', None) + + # Look for "activate_this.py" in this path, which is installed by VirtualEnv. + # This mechanism is used by vpython as well to sanitize VirtualEnvs from + # $PATH. + os.environ['PATH'] = os.pathsep.join([ + p for p in os.environ.get('PATH', '').split(os.pathsep) + if not os.path.isfile(os.path.join(p, 'activate_this.py')) + ]) + def main(args): + # Prune all evidence of VPython/VirtualEnv out of the environment. This means + # that we 'unwrap' vpython VirtualEnv path/env manipulation. Invocations of + # `python` from GN should never inherit the gn.py's own VirtualEnv. This also + # helps to ensure that generated ninja files do not reference python.exe from + # the VirtualEnv generated from depot_tools' own .vpython file (or lack + # thereof), but instead reference the default python from the PATH. + PruneVirtualEnv() + mbw = MetaBuildWrapper() return mbw.Main(args) diff --git a/chromium/tools/metrics/actions/actions.xml b/chromium/tools/metrics/actions/actions.xml index 63b12be472b..4ad24223407 100644 --- a/chromium/tools/metrics/actions/actions.xml +++ b/chromium/tools/metrics/actions/actions.xml @@ -6256,6 +6256,9 @@ should be able to be added at any place in this file. </action> <action name="GridTabSwitcher.Drag.AddToGroupOrCreateGroup"> + <obsolete> + Deprecated as of 8/2019. Replaced with TabGroup.Created.DropToMerge. + </obsolete> <owner>yusufo@chromium.org</owner> <owner>wychen@chromium.org</owner> <description> @@ -12225,6 +12228,9 @@ should be able to be added at any place in this file. </action> <action name="MobileNewTabOpenedTabStrip"> + <obsolete> + Deprecated as of 8/2019. Replaced with MobileNewTabOpened.TabStrip. + </obsolete> <owner>yusufo@chromium.org</owner> <owner>wychen@chromium.org</owner> <description> @@ -21306,7 +21312,16 @@ should be able to be added at any place in this file. <description>User drags a tab to reorder it.</description> </action> +<action name="TabGridDialog"> + <owner>yusufo@chromium.org</owner> + <owner>wychen@chromium.org</owner> + <description>User took an action on the TabGridDialog.</description> +</action> + <action name="TabGridDialog.Drag.RemoveFromGroup"> + <obsolete> + Deprecated as of 8/2019. Replaced with TabGrid.Drag.RemoveFromGroup. + </obsolete> <owner>yusufo@chromium.org</owner> <owner>wychen@chromium.org</owner> <description> @@ -21333,8 +21348,8 @@ should be able to be added at any place in this file. <owner>yusufo@chromium.org</owner> <owner>wychen@chromium.org</owner> <description> - Users tapped "^" button on the tab strip, causing tab group bottom - sheet to be shown. + Users tapped "^" button on the tab strip, causing tab group + component to be shown. </description> </action> @@ -22853,6 +22868,9 @@ should be able to be added at any place in this file. <action-suffix separator="." ordering="suffix"> <suffix name="DeveloperRequestedNewTab" label="Users tapped on a link with a target=_blank"/> + <suffix name="DropToMerge" + label="Users dragged a single tab and dropped it on another single tab + to create a group."/> <suffix name="OpenInNewTab" label="Users long pressed a link and opened in new tab."/> <suffix name="TabMultiSelect" @@ -22865,6 +22883,10 @@ should be able to be added at any place in this file. <action-suffix separator="." ordering="suffix"> <suffix name="GridTabSwitcher" label="Users switched tabs from the grid layout TabSwitcher."/> + <suffix name="TabGridDialogFromStrip" + label="Users switched tabs within the tab grid dialog."/> + <suffix name="TabGridDialogInSwitcher" + label="Users switched tabs between groups using tab grid dialog."/> <suffix name="TabGridSheet" label="Users switched tabs from the tab group in the bottom sheet switcher."/> @@ -22873,6 +22895,14 @@ should be able to be added at any place in this file. </action-suffix> <action-suffix separator="." ordering="suffix"> + <suffix name="TabGridDialogFromStrip" + label="Users tapped '+' button on the tab group dialog toolbar, causing + a new tab to be created in the group. The dialog is expanded + from tab strip"/> + <suffix name="TabGridDialogInSwitcher" + label="Users tapped '+' button on the tab group dialog toolbar, causing + a new tab to be created in the group. The dialog is in tab + switcher."/> <suffix name="TabGridSheet" label="Users tapped '+' button on the tab group bottom sheet toolbar causes a new tab to be created in the group."/> @@ -22916,25 +22946,47 @@ should be able to be added at any place in this file. <action-suffix separator="." ordering="suffix"> <suffix name="GridTabSwitcher" label="Users swipe to close a tab in grid layout TabSwitcher."/> - <suffix name="TabGridDialog" - label="Users swipe to close a tab in tab grid dialog."/> + <suffix name="TabGridDialogFromStrip" + label="Users swipe to close a tab in tab grid dialog from tab strip."/> + <suffix name="TabGridDialogInSwitcher" + label="Users swipe to close a tab in tab grid dialog from grid tab + switcher."/> <suffix name="TabGridSheet" label="Users swipe to close a tab in tab group bottom sheet."/> <affected-action name="MobileStackViewSwipeCloseTab"/> </action-suffix> <action-suffix separator="." ordering="suffix"> + <suffix name="DropToMerge" + label="Users use drop-to-merge to combine a single tab and a group or + two groups."/> + <suffix name="RemoveFromGroup.TabGridDialogFromStrip" + label="Users ungroup a tab by dropping it on the ungroup bar in tab + grid dialog from tab strip."/> + <suffix name="RemoveFromGroup.TabGridDialogInSwitcher" + label="Users ungroup a tab by dropping it on the ungroup bar in tab + grid dialog from grid tab switcher."/> <suffix name="Reordered.GridTabSwitcher" - label="Users drag to reorder tabs in grid layout TabSwitcher."/> - <suffix name="Reordered.TabGridDialog" - label="Users drag to reorder tabs in tab grid dialog."/> + label="Users drag to reorder tabs in grid layout TabSwitcher. Recorded + at each mini reordering during the gesture."/> + <suffix name="Reordered.TabGridDialogFromStrip" + label="Users drag to reorder tabs in tab grid dialog from tab strip. + Recorded at each mini reordering during the gesture."/> + <suffix name="Reordered.TabGridDialogInSwitcher" + label="Users drag to reorder tabs in tab grid dialog from grid tab + switcher. Recorded at each mini reordering during the gesture."/> <suffix name="Reordered.TabGridSheet" - label="Users drag to reorder tabs in tab group bottom sheet."/> + label="Users drag to reorder tabs in tab group bottom sheet. Recorded + at each mini reordering during the gesture."/> <suffix name="Start.GridTabSwitcher" label="Users long tap on a tab to trigger dragging in grid layout TabSwitcher."/> - <suffix name="Start.TabGridDialog" - label="Users long tap on a tab to trigger dragging in tab grid dialog."/> + <suffix name="Start.TabGridDialogFromStrip" + label="Users long tap on a tab to trigger dragging in tab grid dialog + from tab strip."/> + <suffix name="Start.TabGridDialogInSwitcher" + label="Users long tap on a tab to trigger dragging in tab grid dialog + from grid tab switcher."/> <suffix name="Start.TabGridSheet" label="Users long tap on a tab to trigger dragging in tab group bottom sheet."/> @@ -22954,4 +23006,42 @@ should be able to be added at any place in this file. <affected-action name="TabMultiSelect"/> </action-suffix> +<action-suffix separator="." ordering="suffix"> + <suffix name="LongTapMenu" label="Users closed a tab by long tap menu"/> + <affected-action name="MobileMenuCloseTab"/> +</action-suffix> + +<action-suffix separator="." ordering="suffix"> + <suffix name="AppMenu" label="Users opened a new tab by app menu"/> + <suffix name="LongTapMenu" label="Users opened a new tab by long tap menu"/> + <affected-action name="MobileMenuNewTab"/> +</action-suffix> + +<action-suffix separator="." ordering="suffix"> + <suffix name="AppMenu" label="Users opened a new incognito tab by app menu"/> + <suffix name="LongTapMenu" + label="Users opened a new incognito tab by long tap menu"/> + <affected-action name="MobileMenuNewIncognitoTab"/> +</action-suffix> + +<action-suffix separator="." ordering="suffix"> + <suffix name="TabGridDialog" + label="Users tapped expand button on the tab strip, causing tab grid + dialog to be shown."/> + <suffix name="TabGridSheet" + label="Users tapped expand button on the tab strip, causing tab grid + sheet to be shown."/> + <affected-action name="TabGroup.ExpandedFromStrip"/> +</action-suffix> + +<action-suffix separator="." ordering="suffix"> + <suffix name="Exit" + label="Users click the back button or the scrim to exit the tab group + dialog."/> + <suffix name="ExpandedFromSwitcher" + label="Users click a card in TabSwitcher to open a dialog. See also + TabGroup.ExpandedFromStrip.TabGridDialog."/> + <affected-action name="TabGridDialog"/> +</action-suffix> + </actions> diff --git a/chromium/tools/metrics/histograms/enums.xml b/chromium/tools/metrics/histograms/enums.xml index 8a646ec6d79..a3742c46b76 100644 --- a/chromium/tools/metrics/histograms/enums.xml +++ b/chromium/tools/metrics/histograms/enums.xml @@ -31813,6 +31813,20 @@ Called by update_gpu_driver_bug_workaround_entries.py.--> <int value="3" label="App Bundle URL, not handled"/> </enum> +<enum name="IOSJavaScriptDialogDismissalCause"> + <int value="0" label="Tab closed">The tab owning the dialog was closed</int> + <int value="1" label="Closed by user"> + The user tapped on the OK or Cancel button. + </int> + <int value="2" label="Blocked by user"> + Dialog was blocked because the user tapped the "Suppress Dialogs" + option. + </int> + <int value="3" label="Closed for navigation"> + The user navigated the tab (e.g. back/forward, reload) + </int> +</enum> + <enum name="IosLocationAuthorizationStatus"> <int value="0" label="User has not made a choice"/> <int value="1" label="Restricted"/> @@ -64101,6 +64115,26 @@ Full version information for the fingerprint enum values: </int> </enum> +<enum name="ZeroSuggestEligibleOnFocusForRemoteNoUrlOnNTP"> + <int value="0" label="Eligible"/> + <int value="1" label="Ineligible: Off the Record"> + The user is in incognito mode. + </int> + <int value="2" label="Ineligible: Suggestions disabled"> + Suggestion mechanism is disabled by user. + </int> + <int value="3" label="Ineligible: User not authenticated"> + User is not signed in. + </int> + <int value="4" label="Ineligible: Unsupported suggestion server"> + User is using an unsupported search engine. + </int> + <int value="5" label="Ineligible: Not participating"> + User is not participating in an experiment that enables RemoteNoURL + suggestions on the NTP. + </int> +</enum> + </enums> </histogram-configuration> diff --git a/chromium/tools/metrics/histograms/histograms.xml b/chromium/tools/metrics/histograms/histograms.xml index eb0f5a6c940..9a5e6f5dfbf 100644 --- a/chromium/tools/metrics/histograms/histograms.xml +++ b/chromium/tools/metrics/histograms/histograms.xml @@ -53310,6 +53310,13 @@ uploading your change for review. </summary> </histogram> +<histogram name="IOS.Dialogs.JavaScriptDialogClosed" + enum="IOSJavaScriptDialogDismissalCause" expires_after="M80"> + <owner>kkhorimoto@chromium.org</owner> + <owner>michaeldo@chromium.org</owner> + <summary>Tracks the way JavaScript dialogs are closed on iOS.</summary> +</histogram> + <histogram name="IOS.DragAndDrop.DragContent" enum="DragContent" expires_after="M77"> <owner>jif@chromium.org</owner> @@ -89439,6 +89446,25 @@ uploading your change for review. </summary> </histogram> +<histogram name="Omnibox.SuggestionDeleted.ProviderAndResultType" + enum="OmniboxProviderAndResultType" expires_after="2020-09-01"> + <owner>mpearson@chromium.org</owner> + <owner>jdonnelly@chromium.org</owner> + <owner>ender@chromium.org</owner> + <summary> + The provider and result type of the suggestion the user deleted. In case of + duplicate suggestions, only the displayed one (the one chosen as canonical + representative) is considered. As such, some deletion requests will not be + recorded here. This is because if something in the set of duplicates was + deletable then Chrome was willing to allow a delete action to happen on the + set. However, if the canonical item is not deletable, the histogram will not + be emitted. + + This histogram records all user suggestion delete interactions, both local + and remote. + </summary> +</histogram> + <histogram name="Omnibox.SuggestionUsed.AnswerInSuggest" enum="SuggestionAnswerOptionalType" expires_after="M83"> <owner>chrome-android-omnibox-team@google.com</owner> @@ -90030,6 +90056,42 @@ uploading your change for review. </summary> </histogram> +<histogram name="Omnibox.ZeroSuggest.Eligible.RemoteNoUrl.OnNTP.OnFocus" + enum="ZeroSuggestEligibleOnFocusForRemoteNoUrlOnNTP" + expires_after="2020-01-20"> + <owner>mpearson@chromium.org</owner> + <owner>jdonnelly@chromium.org</owner> + <owner>ender@chromium.org</owner> + <summary> + Whether the user is eligible to receive personalized zero suggestions on the + NTP. This histogram is updated only when the user focuses the omnibox on the + NTP and is emitted only once even if multiple criteria make a user + ineligible. Consult the source code to learn more about the precedence of + these values. + + This metric is recorded for all contexts (including users non-syncing, in + incognito mode, or using different search engines) and targets specifically + eligibility in the current situation. + </summary> +</histogram> + +<histogram name="Omnibox.ZeroSuggest.Eligible.RemoteNoUrl.OnNTP.OnProfileOpen" + enum="ZeroSuggestEligibleOnFocusForRemoteNoUrlOnNTP" + expires_after="2020-01-20"> + <owner>mpearson@chromium.org</owner> + <owner>jdonnelly@chromium.org</owner> + <owner>ender@chromium.org</owner> + <summary> + Whether the user is eligible to receive personalized zero suggestions on the + NTP. This histogram is updated every time Chrome registers a profile change. + + This metric is recorded for all contexts (including users non-syncing, in + incognito mode, or using different search engines) and targets overall + profile eligibility to receive zero-prefix query suggestions on a New Tab + Page. + </summary> +</histogram> + <histogram name="Omnibox.ZeroSuggest.MostVisitedResultsCounterfactual"> <owner>mpearson@chromium.org</owner> <owner>jdonnelly@chromium.org</owner> @@ -105704,6 +105766,26 @@ uploading your change for review. </summary> </histogram> +<histogram name="Previews.DeferAllScript.DenyListMatch" enum="BooleanDetected" + expires_after="2020-01-20"> + <owner>tbansal@chromium.org</owner> + <owner>dougarnett@chromium.org</owner> + <summary> + Records true when a URL matches the regex of denylist. Recorded at the time + of navigation commit. + </summary> +</histogram> + +<histogram name="Previews.DeferAllScript.RedirectLoopDetectedUsingCache" + enum="BooleanDetected" expires_after="2020-01-20"> + <owner>tbansal@chromium.org</owner> + <owner>dougarnett@chromium.org</owner> + <summary> + Records true when a redirect loop is detected when defer all script is in + use. Recorded at the time of navigation commit. + </summary> +</histogram> + <histogram name="Previews.EligibilityReason" enum="PreviewsEligibilityReason" expires_after="2020-01-20"> <owner>ryansturm@chromium.org</owner> diff --git a/chromium/ui/accessibility/ax_event_generator.cc b/chromium/ui/accessibility/ax_event_generator.cc index 6af53ea7c69..bedd582557e 100644 --- a/chromium/ui/accessibility/ax_event_generator.cc +++ b/chromium/ui/accessibility/ax_event_generator.cc @@ -143,9 +143,9 @@ void AXEventGenerator::AddEvent(AXNode* node, AXEventGenerator::Event event) { node_events.emplace(event, ax::mojom::EventFrom::kNone); } -void AXEventGenerator::OnNodeDataWillChange(AXTree* tree, - const AXNodeData& old_node_data, - const AXNodeData& new_node_data) { +void AXEventGenerator::OnNodeDataChanged(AXTree* tree, + const AXNodeData& old_node_data, + const AXNodeData& new_node_data) { DCHECK_EQ(tree_, tree); // Fire CHILDREN_CHANGED events when the list of children updates. // Internally we store inline text box nodes as children of a static text @@ -179,8 +179,6 @@ void AXEventGenerator::OnStateChanged(AXTree* tree, case ax::mojom::State::kExpanded: AddEvent(node, new_value ? Event::EXPANDED : Event::COLLAPSED); - // TODO(dtseng): tree in the midst of updates. Disallow access to - // |node|. if (node->data().role == ax::mojom::Role::kRow || node->data().role == ax::mojom::Role::kTreeItem) { AXNode* container = node; @@ -248,8 +246,6 @@ void AXEventGenerator::OnStringAttributeChanged(AXTree* tree, // Fire a LIVE_REGION_CREATED if the previous value was off, and the new // value is not-off. - // TODO(dtseng): tree in the midst of updates. Disallow access to - // |node|. if (!IsAlert(node->data().role)) { bool old_state = !old_value.empty() && old_value != "off"; bool new_state = !new_value.empty() && new_value != "off"; @@ -263,8 +259,6 @@ void AXEventGenerator::OnStringAttributeChanged(AXTree* tree, if (node != tree->root()) AddEvent(node, Event::NAME_CHANGED); - // TODO(dtseng): tree in the midst of updates. Disallow - // access to |node|. if (node->data().HasStringAttribute( ax::mojom::StringAttribute::kContainerLiveStatus)) { FireLiveRegionEvents(node); diff --git a/chromium/ui/accessibility/ax_event_generator.h b/chromium/ui/accessibility/ax_event_generator.h index a2e89a99e9a..4016e915fe9 100644 --- a/chromium/ui/accessibility/ax_event_generator.h +++ b/chromium/ui/accessibility/ax_event_generator.h @@ -165,9 +165,9 @@ class AX_EXPORT AXEventGenerator : public AXTreeObserver { protected: // AXTreeObserver overrides. - void OnNodeDataWillChange(AXTree* tree, - const AXNodeData& old_node_data, - const AXNodeData& new_node_data) override; + void OnNodeDataChanged(AXTree* tree, + const AXNodeData& old_node_data, + const AXNodeData& new_node_data) override; void OnRoleChanged(AXTree* tree, AXNode* node, ax::mojom::Role old_role, diff --git a/chromium/ui/accessibility/ax_node.cc b/chromium/ui/accessibility/ax_node.cc index f818c1f4163..88c61870a9d 100644 --- a/chromium/ui/accessibility/ax_node.cc +++ b/chromium/ui/accessibility/ax_node.cc @@ -34,6 +34,7 @@ AXNode::AXNode(AXNode::OwnerTree* tree, AXNode::~AXNode() = default; size_t AXNode::GetUnignoredChildCount() const { + DCHECK(!tree_->GetTreeUpdateInProgressState()); return unignored_child_count_; } @@ -42,6 +43,7 @@ AXNodeData&& AXNode::TakeData() { } AXNode* AXNode::GetUnignoredChildAtIndex(size_t index) const { + DCHECK(!tree_->GetTreeUpdateInProgressState()); size_t count = 0; for (auto it = UnignoredChildrenBegin(); it != UnignoredChildrenEnd(); ++it) { if (count == index) @@ -52,6 +54,7 @@ AXNode* AXNode::GetUnignoredChildAtIndex(size_t index) const { } AXNode* AXNode::GetUnignoredParent() const { + DCHECK(!tree_->GetTreeUpdateInProgressState()); AXNode* result = parent(); while (result && result->data().HasState(ax::mojom::State::kIgnored)) result = result->parent(); @@ -59,14 +62,17 @@ AXNode* AXNode::GetUnignoredParent() const { } size_t AXNode::GetUnignoredIndexInParent() const { + DCHECK(!tree_->GetTreeUpdateInProgressState()); return unignored_index_in_parent_; } AXNode* AXNode::GetFirstUnignoredChild() const { + DCHECK(!tree_->GetTreeUpdateInProgressState()); return ComputeFirstUnignoredChildRecursive(); } AXNode* AXNode::GetLastUnignoredChild() const { + DCHECK(!tree_->GetTreeUpdateInProgressState()); return ComputeLastUnignoredChildRecursive(); } @@ -95,6 +101,7 @@ AXNode* AXNode::GetDeepestLastUnignoredChild() const { } AXNode* AXNode::GetNextUnignoredSibling() const { + DCHECK(!tree_->GetTreeUpdateInProgressState()); AXNode* parent_node = parent(); size_t index = index_in_parent() + 1; while (parent_node) { @@ -122,6 +129,7 @@ AXNode* AXNode::GetNextUnignoredSibling() const { } AXNode* AXNode::GetPreviousUnignoredSibling() const { + DCHECK(!tree_->GetTreeUpdateInProgressState()); AXNode* parent_node = parent(); bool before_first_child = index_in_parent() <= 0; size_t index = index_in_parent() - 1; @@ -179,10 +187,12 @@ AXNode* AXNode::GetPreviousUnignoredInTreeOrder() const { } AXNode::UnignoredChildIterator AXNode::UnignoredChildrenBegin() const { + DCHECK(!tree_->GetTreeUpdateInProgressState()); return UnignoredChildIterator(this, GetFirstUnignoredChild()); } AXNode::UnignoredChildIterator AXNode::UnignoredChildrenEnd() const { + DCHECK(!tree_->GetTreeUpdateInProgressState()); return UnignoredChildIterator(this, nullptr); } @@ -700,6 +710,10 @@ base::Optional<int> AXNode::GetPosInSet() { return base::nullopt; } + if (data().HasState(ax::mojom::State::kIgnored)) { + return base::nullopt; + } + const AXNode* ordered_set = GetOrderedSet(); if (!ordered_set) { return base::nullopt; @@ -720,6 +734,10 @@ base::Optional<int> AXNode::GetSetSize() { if (!(IsOrderedSetItem() || IsOrderedSet())) return base::nullopt; + if (data().HasState(ax::mojom::State::kIgnored)) { + return base::nullopt; + } + // If node is item-like, find its outerlying ordered set. Otherwise, // this node is the ordered set. const AXNode* ordered_set = this; @@ -812,6 +830,7 @@ AXNode* AXNode::GetOrderedSet() const { } AXNode* AXNode::ComputeLastUnignoredChildRecursive() const { + DCHECK(!tree_->GetTreeUpdateInProgressState()); if (children().size() == 0) return nullptr; @@ -828,6 +847,7 @@ AXNode* AXNode::ComputeLastUnignoredChildRecursive() const { } AXNode* AXNode::ComputeFirstUnignoredChildRecursive() const { + DCHECK(!tree_->GetTreeUpdateInProgressState()); for (size_t i = 0; i < children().size(); i++) { AXNode* child = children_[i]; if (!child->data().HasState(ax::mojom::State::kIgnored)) diff --git a/chromium/ui/accessibility/ax_node_position.cc b/chromium/ui/accessibility/ax_node_position.cc index 7fb00c3947e..4666187473a 100644 --- a/chromium/ui/accessibility/ax_node_position.cc +++ b/chromium/ui/accessibility/ax_node_position.cc @@ -43,6 +43,30 @@ base::string16 AXNodePosition::GetText() const { return text; } +int AXNodePosition::MaxTextOffset() const { + if (IsNullPosition()) + return INVALID_INDEX; + + const AXNode* anchor = GetAnchor(); + DCHECK(anchor); + const std::string& value = + anchor->data().GetStringAttribute(ax::mojom::StringAttribute::kValue); + if (!value.empty()) + return value.length(); + + if (anchor->IsText()) { + return anchor->data() + .GetStringAttribute(ax::mojom::StringAttribute::kName) + .length(); + } + + int max_text_offset = 0; + for (int i = 0; i < AnchorChildCount(); ++i) + max_text_offset += CreateChildPositionAt(i)->MaxTextOffset(); + + return max_text_offset; +} + // static AXNodePosition::AXPositionInstance AXNodePosition::CreatePosition( AXTreeID tree_id, diff --git a/chromium/ui/accessibility/ax_node_position.h b/chromium/ui/accessibility/ax_node_position.h index 31ae32ee59c..4da288035df 100644 --- a/chromium/ui/accessibility/ax_node_position.h +++ b/chromium/ui/accessibility/ax_node_position.h @@ -34,6 +34,7 @@ class AX_EXPORT AXNodePosition : public AXPosition<AXNodePosition, AXNode> { AXPositionInstance Clone() const override; + int MaxTextOffset() const override; bool IsInLineBreak() const override; bool IsInTextObject() const override; bool IsInWhiteSpace() const override; diff --git a/chromium/ui/accessibility/ax_node_position_unittest.cc b/chromium/ui/accessibility/ax_node_position_unittest.cc index a57c4f8e029..25466296878 100644 --- a/chromium/ui/accessibility/ax_node_position_unittest.cc +++ b/chromium/ui/accessibility/ax_node_position_unittest.cc @@ -50,6 +50,38 @@ class AXPositionTest : public testing::Test { void SetUp() override; void TearDown() override; + void AssertTextLengthEquals(const AXTree* tree, + int32_t node_id, + int expected_text_length) { + TestPositionType text_position = AXNodePosition::CreateTextPosition( + tree->data().tree_id, node_id, 0 /* text_offset */, + ax::mojom::TextAffinity::kUpstream); + ASSERT_NE(nullptr, text_position); + ASSERT_TRUE(text_position->IsTextPosition()); + ASSERT_EQ(expected_text_length, text_position->MaxTextOffset()); + ASSERT_EQ(expected_text_length, + static_cast<int>(text_position->GetText().length())); + } + + // Creates a new AXTree from a vector of nodes. + // Assumes the first node in the vector is the root. + std::unique_ptr<AXTree> CreateAXTree( + const std::vector<ui::AXNodeData>& nodes) { + ui::AXTreeUpdate update; + ui::AXTreeData tree_data; + tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID(); + update.tree_data = tree_data; + update.has_tree_data = true; + update.root_id = nodes[0].id; + update.nodes = nodes; + + tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID(); + update.tree_data = tree_data; + std::unique_ptr<AXTree> tree = std::make_unique<AXTree>(update); + AXNodePosition::SetTree(tree.get()); + return tree; + } + AXNodeData root_; AXNodeData button_; AXNodeData check_box_; @@ -276,6 +308,60 @@ TEST_F(AXPositionTest, Clone) { EXPECT_EQ(AXNodePosition::INVALID_INDEX, copy_position->child_index()); } +TEST_F(AXPositionTest, Serialize) { + TestPositionType null_position = AXNodePosition::CreateNullPosition(); + ASSERT_NE(nullptr, null_position); + TestPositionType copy_position = + AXNodePosition::Unserialize(null_position->Serialize()); + ASSERT_NE(nullptr, copy_position); + EXPECT_TRUE(copy_position->IsNullPosition()); + + TestPositionType tree_position = AXNodePosition::CreateTreePosition( + tree_.data().tree_id, root_.id, 1 /* child_index */); + ASSERT_NE(nullptr, tree_position); + copy_position = AXNodePosition::Unserialize(tree_position->Serialize()); + ASSERT_NE(nullptr, copy_position); + EXPECT_TRUE(copy_position->IsTreePosition()); + EXPECT_EQ(root_.id, copy_position->anchor_id()); + EXPECT_EQ(1, copy_position->child_index()); + EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset()); + + tree_position = AXNodePosition::CreateTreePosition( + tree_.data().tree_id, root_.id, AXNodePosition::BEFORE_TEXT); + ASSERT_NE(nullptr, tree_position); + copy_position = AXNodePosition::Unserialize(tree_position->Serialize()); + ASSERT_NE(nullptr, copy_position); + EXPECT_TRUE(copy_position->IsTreePosition()); + EXPECT_EQ(root_.id, copy_position->anchor_id()); + EXPECT_EQ(AXNodePosition::BEFORE_TEXT, copy_position->child_index()); + EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset()); + + TestPositionType text_position = AXNodePosition::CreateTextPosition( + tree_.data().tree_id, text_field_.id, 0 /* text_offset */, + ax::mojom::TextAffinity::kUpstream); + ASSERT_NE(nullptr, text_position); + ASSERT_TRUE(text_position->IsTextPosition()); + copy_position = AXNodePosition::Unserialize(text_position->Serialize()); + ASSERT_NE(nullptr, copy_position); + EXPECT_TRUE(copy_position->IsTextPosition()); + EXPECT_EQ(text_field_.id, copy_position->anchor_id()); + EXPECT_EQ(0, copy_position->text_offset()); + EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, copy_position->affinity()); + + text_position = AXNodePosition::CreateTextPosition( + tree_.data().tree_id, text_field_.id, 0 /* text_offset */, + ax::mojom::TextAffinity::kDownstream); + ASSERT_NE(nullptr, text_position); + ASSERT_TRUE(text_position->IsTextPosition()); + copy_position = AXNodePosition::Unserialize(text_position->Serialize()); + ASSERT_NE(nullptr, copy_position); + EXPECT_TRUE(copy_position->IsTextPosition()); + EXPECT_EQ(text_field_.id, copy_position->anchor_id()); + EXPECT_EQ(0, copy_position->text_offset()); + EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, copy_position->affinity()); + EXPECT_EQ(AXNodePosition::INVALID_INDEX, copy_position->child_index()); +} + TEST_F(AXPositionTest, GetTextFromNullPosition) { TestPositionType text_position = AXNodePosition::CreateNullPosition(); ASSERT_NE(nullptr, text_position); @@ -416,6 +502,49 @@ TEST_F(AXPositionTest, GetMaxTextOffsetFromLineBreak) { ASSERT_EQ(1, text_position->MaxTextOffset()); } +TEST_F(AXPositionTest, GetMaxTextOffsetUpdate) { + AXNodePosition::SetTree(nullptr); + + ui::AXNodeData root_data; + root_data.id = 1; + root_data.role = ax::mojom::Role::kRootWebArea; + + ui::AXNodeData text_data; + text_data.id = 2; + text_data.role = ax::mojom::Role::kStaticText; + text_data.SetName("some text"); + + ui::AXNodeData more_text_data; + more_text_data.id = 3; + more_text_data.role = ax::mojom::Role::kStaticText; + more_text_data.SetName("more text"); + + root_data.child_ids = {2, 3}; + + std::unique_ptr<AXTree> new_tree = + CreateAXTree({root_data, text_data, more_text_data}); + + AssertTextLengthEquals(new_tree.get(), text_data.id, 9); + AssertTextLengthEquals(new_tree.get(), root_data.id, 18); + + text_data.SetName("Adjusted line 1"); + new_tree = CreateAXTree({root_data, text_data, more_text_data}); + AssertTextLengthEquals(new_tree.get(), text_data.id, 15); + AssertTextLengthEquals(new_tree.get(), root_data.id, 24); + + // Value should override name + text_data.SetValue("Value should override name"); + new_tree = CreateAXTree({root_data, text_data, more_text_data}); + AssertTextLengthEquals(new_tree.get(), text_data.id, 26); + AssertTextLengthEquals(new_tree.get(), root_data.id, 35); + + // An empty value should fall back to name + text_data.SetValue(""); + new_tree = CreateAXTree({root_data, text_data, more_text_data}); + AssertTextLengthEquals(new_tree.get(), text_data.id, 15); + AssertTextLengthEquals(new_tree.get(), root_data.id, 24); +} + TEST_F(AXPositionTest, AtStartOfAnchorWithNullPosition) { TestPositionType null_position = AXNodePosition::CreateNullPosition(); ASSERT_NE(nullptr, null_position); @@ -1487,20 +1616,20 @@ TEST_F(AXPositionTest, CreatePositionAtFormatBoundaryWithTextPosition) { AXNodePosition::SetTree(nullptr); AXNodeData root_data; - root_data.id = 0; + root_data.id = 1; root_data.role = ax::mojom::Role::kRootWebArea; AXNodeData text_data; - text_data.id = 1; + text_data.id = 2; text_data.role = ax::mojom::Role::kStaticText; text_data.SetName("some text"); AXNodeData more_text_data; - more_text_data.id = 2; + more_text_data.id = 3; more_text_data.role = ax::mojom::Role::kStaticText; more_text_data.SetName("more text"); - root_data.child_ids = {1, 2}; + root_data.child_ids = {text_data.id, more_text_data.id}; AXTreeUpdate update; AXTreeData tree_data; @@ -2793,30 +2922,30 @@ TEST_F(AXPositionTest, CreateNextAnchorPosition) { AXNodePosition::SetTree(nullptr); AXNodeData root_data; - root_data.id = 0; + root_data.id = 1; root_data.role = ax::mojom::Role::kRootWebArea; AXNodeData text_data; - text_data.id = 1; + text_data.id = 2; text_data.role = ax::mojom::Role::kStaticText; text_data.SetName("some text"); AXNodeData text_field_data; - text_field_data.id = 2; + text_field_data.id = 3; text_field_data.role = ax::mojom::Role::kTextField; AXNodeData empty_text_data; - empty_text_data.id = 3; + empty_text_data.id = 4; empty_text_data.role = ax::mojom::Role::kStaticText; empty_text_data.SetName(""); AXNodeData more_text_data; - more_text_data.id = 4; + more_text_data.id = 5; more_text_data.role = ax::mojom::Role::kStaticText; more_text_data.SetName("more text"); - root_data.child_ids = {1, 2, 4}; - text_field_data.child_ids = {3}; + root_data.child_ids = {text_data.id, text_field_data.id, more_text_data.id}; + text_field_data.child_ids = {empty_text_data.id}; AXTreeUpdate update; AXTreeData tree_data; diff --git a/chromium/ui/accessibility/ax_position.h b/chromium/ui/accessibility/ax_position.h index b35a9eb09c5..a2ff769886a 100644 --- a/chromium/ui/accessibility/ax_position.h +++ b/chromium/ui/accessibility/ax_position.h @@ -10,6 +10,7 @@ #include <memory> #include <ostream> #include <string> +#include <type_traits> #include <utility> #include <vector> @@ -133,6 +134,48 @@ class AXPosition { virtual AXPositionInstance Clone() const = 0; + // A serialization of a position as POD. Not for sharing on disk or sharing + // across thread or process boundaries, just for passing a position to an + // API that works with positions as opaque objects. + struct SerializedPosition { + AXPositionKind kind; + int32_t anchor_id; + int child_index; + int text_offset; + ax::mojom::TextAffinity affinity; + char tree_id[33]; + }; + + static_assert(std::is_trivially_copyable<SerializedPosition>::value, + "SerializedPosition must be POD"); + + SerializedPosition Serialize() { + SerializedPosition result; + result.kind = kind_; + + // A tree ID can be serialized as a 32-byte string. + std::string tree_id_string = tree_id_.ToString(); + DCHECK_LE(tree_id_string.size(), 32U); + strncpy(result.tree_id, tree_id_string.c_str(), 32); + result.tree_id[32] = 0; + + result.anchor_id = anchor_id_; + result.child_index = child_index_; + result.text_offset = text_offset_; + result.affinity = affinity_; + return result; + } + + static AXPositionInstance Unserialize( + const SerializedPosition& serialization) { + AXPositionInstance new_position(new AXPositionType()); + new_position->Initialize(serialization.kind, + ui::AXTreeID::FromString(serialization.tree_id), + serialization.anchor_id, serialization.child_index, + serialization.text_offset, serialization.affinity); + return new_position; + } + virtual bool IsIgnoredPosition() const { return false; } virtual AXPositionInstance AsUnignoredTextPosition( @@ -1790,7 +1833,7 @@ class AXPosition { // Returns the length of the text that is present inside the anchor node, // including any text found in descendant text nodes. - int MaxTextOffset() const { + virtual int MaxTextOffset() const { if (IsNullPosition()) return INVALID_INDEX; return static_cast<int>(GetText().length()); @@ -1840,7 +1883,8 @@ class AXPosition { (child_index_ != BEFORE_TEXT && (child_index_ < 0 || child_index_ > AnchorChildCount()))) || (kind_ == AXPositionKind::TEXT_POSITION && - (text_offset_ < 0 || text_offset_ > MaxTextOffset()))) { + (text_offset_ < 0 || + (text_offset > 0 && text_offset_ > MaxTextOffset())))) { // Reset to the null position. kind_ = AXPositionKind::NULL_POSITION; tree_id_ = AXTreeIDUnknown(); diff --git a/chromium/ui/accessibility/ax_table_info_unittest.cc b/chromium/ui/accessibility/ax_table_info_unittest.cc index e4b990ae692..ccc1409d8bb 100644 --- a/chromium/ui/accessibility/ax_table_info_unittest.cc +++ b/chromium/ui/accessibility/ax_table_info_unittest.cc @@ -247,7 +247,7 @@ TEST_F(AXTableInfoTest, AuthorRowAndColumnCountsAreRespected) { initial_state.nodes[0].child_ids = {2}; MakeRow(&initial_state.nodes[1], 2, 0); initial_state.nodes[1].child_ids = {3}; - MakeCell(&initial_state.nodes[2], 2, 0, 1); + MakeCell(&initial_state.nodes[2], 3, 0, 1); AXTree tree(initial_state); AXTableInfo* table_info = GetTableInfo(&tree, tree.root()); diff --git a/chromium/ui/accessibility/ax_tree.cc b/chromium/ui/accessibility/ax_tree.cc index d464561d01e..9c26f68ce8f 100644 --- a/chromium/ui/accessibility/ax_tree.cc +++ b/chromium/ui/accessibility/ax_tree.cc @@ -12,6 +12,7 @@ #include "base/auto_reset.h" #include "base/command_line.h" #include "base/logging.h" +#include "base/no_destructor.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "ui/accessibility/accessibility_switches.h" @@ -28,6 +29,9 @@ namespace ui { namespace { std::string TreeToStringHelper(const AXNode* node, int indent) { + if (!node) + return ""; + return std::accumulate( node->children().cbegin(), node->children().cend(), std::string(2 * indent, ' ') + node->data().ToString() + "\n", @@ -101,27 +105,158 @@ bool IsCollapsed(const AXNode* node) { } // namespace +// This object is used to track structure changes that will occur for a specific +// AXID. This includes how many times we expect that a node with a specific AXID +// will be created and/or destroyed, and how many times a subtree rooted at AXID +// expects to be destroyed during an AXTreeUpdate. +// +// An AXTreeUpdate is a serialized representation of an atomic change to an +// AXTree. See also |AXTreeUpdate| which documents the nature and invariants +// required to atomically update the AXTree. +// +// The reason that we must track these counts, and the reason these are counts +// rather than a bool/flag is because an AXTreeUpdate may contain multiple +// AXNodeData updates for a given AXID. A common way that this occurs is when +// multiple AXTreeUpdates are merged together, combining their AXNodeData list. +// Additionally AXIDs may be reused after being removed from the tree, +// most notably when "reparenting" a node. A "reparent" occurs when an AXID is +// first destroyed from the tree then created again in the same AXTreeUpdate, +// which may also occur multiple times with merged updates. +// +// We need to accumulate these counts for 3 reasons : +// 1. To determine what structure changes *will* occur before applying +// updates to the tree so that we can notify observers of structure changes +// when the tree is still in a stable and unchanged state. +// 2. Capture any errors *before* applying updates to the tree structure +// due to the order of (or lack of) AXNodeData entries in the update +// so we can abort a bad update instead of applying it partway. +// 3. To validate that the expectations we accumulate actually match +// updates that are applied to the tree. +// +// To reiterate the invariants that this structure is taking a dependency on +// from |AXTreeUpdate|, suppose that the next AXNodeData to be applied is +// |node|. The following invariants must hold: +// 1. Either +// a) |node.id| is already in the tree, or +// b) the tree is empty, and +// |node| is the new root of the tree, and +// |node.role| == WebAXRoleRootWebArea. +// 2. Every child id in |node.child_ids| must either be already a child +// of this node, or a new id not previously in the tree. It is not +// allowed to "reparent" a child to this node without first removing +// that child from its previous parent. +// 3. When a new id appears in |node.child_ids|, the tree should create a +// new uninitialized placeholder node for it immediately. That +// placeholder must be updated within the same AXTreeUpdate, otherwise +// it's a fatal error. This guarantees the tree is always complete +// before or after an AXTreeUpdate. +struct PendingStructureChanges { + PendingStructureChanges(const AXNode* node) + : destroy_subtree_count(0), + destroy_node_count(0), + create_node_count(0), + node_exists(!!node), + parent_node_id((node && node->parent()) + ? base::Optional<AXNode::AXID>{node->parent()->id()} + : base::nullopt), + last_known_data(node ? &node->data() : nullptr) {} + + // Returns true if this node has any changes remaining. + // This includes pending subtree or node destruction, and node creation. + bool DoesNodeExpectAnyStructureChanges() const { + return DoesNodeExpectSubtreeWillBeDestroyed() || + DoesNodeExpectNodeWillBeDestroyed() || + DoesNodeExpectNodeWillBeCreated(); + } + + // Returns true if there are any pending changes that require destroying + // this node or its subtree. + bool DoesNodeExpectSubtreeOrNodeWillBeDestroyed() const { + return DoesNodeExpectSubtreeWillBeDestroyed() || + DoesNodeExpectNodeWillBeDestroyed(); + } + + // Returns true if the subtree rooted at this node needs to be destroyed + // during the update, but this may not be the next action that needs to be + // performed on the node. + bool DoesNodeExpectSubtreeWillBeDestroyed() const { + return destroy_subtree_count; + } + + // Returns true if this node needs to be destroyed during the update, but this + // may not be the next action that needs to be performed on the node. + bool DoesNodeExpectNodeWillBeDestroyed() const { return destroy_node_count; } + + // Returns true if this node needs be created during the update, but this + // may not be the next action that needs to be performed on the node. + bool DoesNodeExpectNodeWillBeCreated() const { return create_node_count; } + + // Returns true if this node would exist in the tree as of the last pending + // update that was processed, and the node has not been provided node data. + bool DoesNodeRequireInit() const { return node_exists && !last_known_data; } + + // Keep track of the number of times the subtree rooted at this node + // will be destroyed. + // An example of when this count may be larger than 1 is if updates were + // merged together. A subtree may be [created,] destroyed, created, and + // destroyed again within the same |AXTreeUpdate|. The important takeaway here + // is that an update may request destruction of a subtree rooted at an + // AXID more than once, not that a specific subtree is being destroyed + // more than once. + int32_t destroy_subtree_count; + + // Keep track of the number of times this node will be destroyed. + // An example of when this count may be larger than 1 is if updates were + // merged together. A node may be [created,] destroyed, created, and destroyed + // again within the same |AXTreeUpdate|. The important takeaway here is that + // an AXID may request destruction more than once, not that a specific node + // is being destroyed more than once. + int32_t destroy_node_count; + + // Keep track of the number of times this node will be created. + // An example of when this count may be larger than 1 is if updates were + // merged together. A node may be [destroyed,] created, destroyed, and created + // again within the same |AXTreeUpdate|. The important takeaway here is that + // an AXID may request creation more than once, not that a specific node is + // being created more than once. + int32_t create_node_count; + + // Keep track of whether this node exists in the tree as of the last pending + // update that was processed. + bool node_exists; + + // Keep track of the parent id for this node as of the last pending + // update that was processed. + base::Optional<AXNode::AXID> parent_node_id; + + // Keep track of the last known node data for this node. + // This will be null either when a node does not exist in the tree, or + // when the node is new and has not been initialized with node data yet. + // This is needed to determine what children have changed between pending + // updates. + const AXNodeData* last_known_data; +}; + // Intermediate state to keep track of during a tree update. struct AXTreeUpdateState { - AXTreeUpdateState() : new_root(nullptr) {} - // Returns whether this update changes |node|. - bool IsChangedNode(const AXNode* node) { - return changed_node_ids.find(node->id()) != changed_node_ids.end(); - } + AXTreeUpdateState(const AXTree& tree) + : computing_pending_changes(false), + root_will_be_created(false), + tree(tree) {} // Returns whether this update removes |node|. bool IsRemovedNode(const AXNode* node) const { - return removed_node_ids.find(node->id()) != removed_node_ids.end(); + return base::Contains(removed_node_ids, node->id()); } // Returns whether this update creates |node|. - bool IsNewNode(const AXNode* node) { - return new_nodes.find(node) != new_nodes.end(); + bool IsCreatedNode(const AXNode* node) const { + return base::Contains(new_node_ids, node->id()); } // If this node is removed, it should be considered reparented. bool IsPotentiallyReparentedNode(const AXNode* node) const { - return base::Contains(potentially_reparented_ids, node->id()); + return base::Contains(node_ids_found_in_update, node->id()); } // Returns whether this update reparents |node|. @@ -129,49 +264,281 @@ struct AXTreeUpdateState { return IsPotentiallyReparentedNode(node) && IsRemovedNode(node); } + // Returns true if the node should exist in the tree but doesn't have + // any node data yet. + bool DoesPendingNodeRequireInit(AXNode::AXID node_id) const { + DCHECK(computing_pending_changes) << "This method should be called before " + "any updates are made to the tree."; + PendingStructureChanges* data = GetPendingStructureChanges(node_id); + return data && data->DoesNodeRequireInit(); + } + + // Returns the parent node id for the pending node. + base::Optional<AXNode::AXID> GetParentIdForPendingNode(AXNode::AXID node_id) { + DCHECK(computing_pending_changes) << "This method should be called before " + "any updates are made to the tree."; + PendingStructureChanges* data = GetOrCreatePendingStructureChanges(node_id); + DCHECK(!data->parent_node_id || + ShouldPendingNodeExistInTree(*data->parent_node_id)); + return data->parent_node_id; + } + + // Returns true if this node should exist in the tree. + bool ShouldPendingNodeExistInTree(AXNode::AXID node_id) { + DCHECK(computing_pending_changes) << "This method should be called before " + "any updates are made to the tree."; + return GetOrCreatePendingStructureChanges(node_id)->node_exists; + } + + // Returns the last known node data for a pending node. + const AXNodeData& GetLastKnownPendingNodeData(AXNode::AXID node_id) const { + DCHECK(computing_pending_changes) << "This method should be called before " + "any updates are made to the tree."; + static base::NoDestructor<ui::AXNodeData> empty_data; + PendingStructureChanges* data = GetPendingStructureChanges(node_id); + return (data && data->last_known_data) ? *data->last_known_data + : *empty_data; + } + + // Clear the last known pending data for |node_id|. + void ClearLastKnownPendingNodeData(AXNode::AXID node_id) { + DCHECK(computing_pending_changes) << "This method should be called before " + "any updates are made to the tree."; + GetOrCreatePendingStructureChanges(node_id)->last_known_data = nullptr; + } + + // Update the last known pending node data for |node_data.id|. + void SetLastKnownPendingNodeData(const AXNodeData* node_data) { + DCHECK(computing_pending_changes) << "This method should be called before " + "any updates are made to the tree."; + GetOrCreatePendingStructureChanges(node_data->id)->last_known_data = + node_data; + } + + // Returns the number of times the update is expected to destroy a + // subtree rooted at |node_id|. + int32_t GetPendingDestroySubtreeCount(AXNode::AXID node_id) const { + DCHECK(!computing_pending_changes) + << "This method should not be called before any updates are made to " + "the tree."; + if (PendingStructureChanges* data = GetPendingStructureChanges(node_id)) + return data->destroy_subtree_count; + return 0; + } + + // Increments the number of times the update is expected to + // destroy a subtree rooted at |node_id|. + // Returns true on success, false on failure when the node will not exist. + bool IncrementPendingDestroySubtreeCount(AXNode::AXID node_id) { + DCHECK(computing_pending_changes) << "This method should be called before " + "any updates are made to the tree."; + PendingStructureChanges* data = GetOrCreatePendingStructureChanges(node_id); + if (!data->node_exists) + return false; + + ++data->destroy_subtree_count; + return true; + } + + // Decrements the number of times the update is expected to + // destroy a subtree rooted at |node_id|. + void DecrementPendingDestroySubtreeCount(AXNode::AXID node_id) { + DCHECK(!computing_pending_changes) + << "This method should not be called before any updates are made to " + "the tree."; + if (PendingStructureChanges* data = GetPendingStructureChanges(node_id)) { + DCHECK_GT(data->destroy_subtree_count, 0); + --data->destroy_subtree_count; + } + } + + // Returns the number of times the update is expected to destroy + // a node with |node_id|. + int32_t GetPendingDestroyNodeCount(AXNode::AXID node_id) const { + DCHECK(!computing_pending_changes) + << "This method should not be called before any updates are made to " + "the tree."; + if (PendingStructureChanges* data = GetPendingStructureChanges(node_id)) + return data->destroy_node_count; + return 0; + } + + // Increments the number of times the update is expected to + // destroy a node with |node_id|. + // Returns true on success, false on failure when the node will not exist. + bool IncrementPendingDestroyNodeCount(AXNode::AXID node_id) { + DCHECK(computing_pending_changes) << "This method should be called before " + "any updates are made to the tree."; + PendingStructureChanges* data = GetOrCreatePendingStructureChanges(node_id); + if (!data->node_exists) + return false; + + ++data->destroy_node_count; + data->node_exists = false; + data->last_known_data = nullptr; + data->parent_node_id = base::nullopt; + if (pending_root_id == node_id) + pending_root_id = base::nullopt; + return true; + } + + // Decrements the number of times the update is expected to + // destroy a node with |node_id|. + void DecrementPendingDestroyNodeCount(AXNode::AXID node_id) { + DCHECK(!computing_pending_changes) + << "This method should not be called before any updates are made to " + "the tree."; + if (PendingStructureChanges* data = GetPendingStructureChanges(node_id)) { + DCHECK_GT(data->destroy_node_count, 0); + --data->destroy_node_count; + } + } + + // Returns the number of times the update is expected to create + // a node with |node_id|. + int32_t GetPendingCreateNodeCount(AXNode::AXID node_id) const { + DCHECK(!computing_pending_changes) + << "This method should not be called before any updates are made to " + "the tree."; + if (PendingStructureChanges* data = GetPendingStructureChanges(node_id)) + return data->create_node_count; + return 0; + } + + // Increments the number of times the update is expected to + // create a node with |node_id|. + // Returns true on success, false on failure when the node will already exist. + bool IncrementPendingCreateNodeCount( + AXNode::AXID node_id, + base::Optional<AXNode::AXID> parent_node_id) { + DCHECK(computing_pending_changes) << "This method should be called before " + "any updates are made to the tree."; + PendingStructureChanges* data = GetOrCreatePendingStructureChanges(node_id); + if (data->node_exists) + return false; + + ++data->create_node_count; + data->node_exists = true; + data->parent_node_id = parent_node_id; + return true; + } + + // Decrements the number of times the update is expected to + // create a node with |node_id|. + void DecrementPendingCreateNodeCount(AXNode::AXID node_id) { + DCHECK(!computing_pending_changes) + << "This method should not be called before any updates are made to " + "the tree."; + if (PendingStructureChanges* data = GetPendingStructureChanges(node_id)) { + DCHECK_GT(data->create_node_count, 0); + --data->create_node_count; + } + } + + // Returns whether this update must invalidate the unignored cached + // values for |node_id|. + bool InvalidatesUnignoredCachedValues(AXNode::AXID node_id) { + return base::Contains(invalidate_unignored_cached_values_ids, node_id); + } + + // Adds the parent of |node_id| to the list of nodes to invalidate unignored + // cached values. + void InvalidateParentNodeUnignoredCacheValues(AXNode::AXID node_id) { + DCHECK(computing_pending_changes) << "This method should be called before " + "any updates are made to the tree."; + base::Optional<AXNode::AXID> parent_node_id = + GetParentIdForPendingNode(node_id); + if (parent_node_id) { + invalidate_unignored_cached_values_ids.insert(*parent_node_id); + } + } + + // Indicates if the tree is calculating what changes will occur during + // an update before the update applies changes. + bool computing_pending_changes; + + // Keeps track of the root node id when calculating what changes will occur + // during an update before the update applies changes. + base::Optional<AXNode::AXID> pending_root_id; + + // Keeps track of whether the root node will need to be created as a new node. + // This may occur either when the root node does not exist before applying + // updates to the tree (new tree), or if the root is the |node_id_to_clear| + // and will be destroyed before applying AXNodeData updates to the tree. + bool root_will_be_created; + // During an update, this keeps track of all nodes that have been // implicitly referenced as part of this update, but haven't been // updated yet. It's an error if there are any pending nodes at the // end of Unserialize. - std::set<const AXNode*> pending_nodes; - - // This is similar to above, but we store node ids here because this list gets - // generated before any nodes get created or re-used. Its purpose is to allow - // us to know what nodes will be updated so we can make more intelligent - // decisions about when to notify observers of removals or reparenting. - std::set<int> changed_node_ids; + std::set<AXNode::AXID> pending_nodes; - // keeps track of nodes whose cached unignored child count, or unignored + // Keeps track of nodes whose cached unignored child count, or unignored // index in parent may have changed, and must be updated. - std::unordered_set<int> invalidate_unignored_cached_values_ids; + std::set<AXNode::AXID> invalidate_unignored_cached_values_ids; - // Potentially reparented node ids include any child node ids touched by the - // update, as well as any new root node id. Nodes are considered - // reparented if they are in this list and removed from somewhere else. - std::set<int> potentially_reparented_ids; + // All child node ids touched by the update, as well as the new root + // node id. Nodes are considered reparented if they are in this list + // and removed from somewhere else. + std::set<AXNode::AXID> node_ids_found_in_update; - std::unordered_set<int> node_data_changed_ids; + // Keeps track of nodes that have changed their node data. + std::set<AXNode::AXID> node_data_changed_ids; // Keeps track of new nodes created during this update. - std::set<const AXNode*> new_nodes; - - // The new root in this update, if any. - AXNode* new_root; + std::set<AXNode::AXID> new_node_ids; + + // Keeps track of any nodes removed. Nodes are removed when their AXID no + // longer exist in the parent |child_ids| list, or the node is part of to the + // subtree of the AXID that was explicitally cleared with |node_id_to_clear|. + // Used to identify re-parented nodes. A re-parented occurs when any AXID + // is first removed from the tree then added to the tree again. + std::set<AXNode::AXID> removed_node_ids; + + // Maps between a node id and its pending update information. + std::map<AXNode::AXID, std::unique_ptr<PendingStructureChanges>> + node_id_to_pending_data; + + // Maps between a node id and the data it owned before being updated. + // We need to keep this around in order to correctly fire post-update events. + std::map<AXNode::AXID, AXNodeData> old_node_id_to_data; + + // Optional copy of the old tree data, only populated when the tree + // data has changed. + base::Optional<AXTreeData> old_tree_data; + + private: + PendingStructureChanges* GetPendingStructureChanges( + AXNode::AXID node_id) const { + auto iter = node_id_to_pending_data.find(node_id); + return (iter != node_id_to_pending_data.cend()) ? iter->second.get() + : nullptr; + } - // Keeps track of any nodes removed. Used to identify re-parented nodes. - std::set<int> removed_node_ids; + PendingStructureChanges* GetOrCreatePendingStructureChanges( + AXNode::AXID node_id) { + auto iter = node_id_to_pending_data.find(node_id); + if (iter == node_id_to_pending_data.cend()) { + const AXNode* node = tree.GetFromId(node_id); + iter = node_id_to_pending_data + .emplace(std::make_pair( + node_id, std::make_unique<PendingStructureChanges>(node))) + .first; + } + return iter->second.get(); + } - // Maps between a node id and its data. We need to keep this around because - // the reparented nodes in this update were actually deleted. - std::map<int32_t, AXNodeData> reparented_node_id_to_data; + // We need to hold onto a reference to the AXTree so that we can + // lazily initialize |PendingStructureChanges| objects. + const AXTree& tree; }; AXTree::AXTree() { AXNodeData root; - root.id = -1; + root.id = AXNode::kInvalidAXID; AXTreeUpdate initial_state; - initial_state.root_id = -1; + initial_state.root_id = AXNode::kInvalidAXID; initial_state.nodes.push_back(root); CHECK(Unserialize(initial_state)) << error(); // TODO(chrishall): should language_detection_manager be a member or pointer? @@ -188,8 +555,12 @@ AXTree::AXTree(const AXTreeUpdate& initial_state) { } AXTree::~AXTree() { - if (root_) + if (root_) { + RecursivelyNotifyNodeWillBeDeleted(root_); + base::AutoReset<bool> update_state_resetter(&tree_update_in_progress_, + true); DestroyNodeAndSubtree(root_, nullptr); + } for (auto& entry : table_info_map_) delete entry.second; table_info_map_.clear(); @@ -398,65 +769,114 @@ const std::set<AXTreeID> AXTree::GetAllChildTreeIds() const { } bool AXTree::Unserialize(const AXTreeUpdate& update) { - // Set update state to true. - // tree_update_in_progress_ gets set back to false whenever this function - // exits. - base::AutoReset<bool> update_state_resetter(&tree_update_in_progress_, true); - - AXTreeUpdateState update_state; - int32_t old_root_id = root_ ? root_->id() : 0; - - // First, make a note of any nodes we will touch as part of this update. - for (size_t i = 0; i < update.nodes.size(); ++i) - update_state.changed_node_ids.insert(update.nodes[i].id); - - if (update.has_tree_data) - UpdateData(update.tree_data); + AXTreeUpdateState update_state(*this); + const AXNode::AXID old_root_id = root_ ? root_->id() : AXNode::kInvalidAXID; // Get all of the node ids that are certain to exist after the update. // These are the nodes that are considered reparented if they are removed from // somewhere else. - update_state.potentially_reparented_ids.emplace(update.root_id); + if (update.root_id != AXNode::kInvalidAXID) + update_state.node_ids_found_in_update.emplace(update.root_id); for (const AXNodeData& update_node_data : update.nodes) { - update_state.potentially_reparented_ids.insert( + update_state.node_ids_found_in_update.insert( update_node_data.child_ids.begin(), update_node_data.child_ids.end()); } + // Accumulates the work that will be required to update the AXTree. + // This allows us to notify observers of structure changes when the + // tree is still in a stable and unchanged state. + if (!ComputePendingChanges(update, update_state)) + return false; + + // Notify observers of subtrees and nodes that are about to be destroyed or + // reparented, this must be done before applying any updates to the tree. + for (auto&& pair : update_state.node_id_to_pending_data) { + const AXNode::AXID node_id = pair.first; + const std::unique_ptr<PendingStructureChanges>& data = pair.second; + if (data->DoesNodeExpectSubtreeOrNodeWillBeDestroyed()) { + if (AXNode* node = GetFromId(node_id)) { + if (data->DoesNodeExpectSubtreeWillBeDestroyed()) + NotifySubtreeWillBeReparentedOrDeleted(node, &update_state); + if (data->DoesNodeExpectNodeWillBeDestroyed()) + NotifyNodeWillBeReparentedOrDeleted(node, &update_state); + } + } + } + + // Notify observers of nodes that are about to change their data. + // This must be done before applying any updates to the tree. + // This is iterating in reverse order so that we only notify once per node id, + // and that we only notify the initial node data against the final node data, + // unless the node is a new root. + std::set<int32_t> notified_node_data_will_change; + for (size_t i = update.nodes.size(); i-- > 0;) { + const AXNodeData& new_data = update.nodes[i]; + const bool is_new_root = + update_state.root_will_be_created && new_data.id == update.root_id; + if (!is_new_root) { + AXNode* node = GetFromId(new_data.id); + if (node && notified_node_data_will_change.insert(new_data.id).second) + NotifyNodeDataWillChange(node->data(), new_data); + } + } + + // Now that we have finished sending events for changes that will happen, + // set update state to true. |tree_update_in_progress_| gets set back to + // false whenever this function exits. + base::AutoReset<bool> update_state_resetter(&tree_update_in_progress_, true); + + // Handle |node_id_to_clear| before applying ordinary node updates. // We distinguish between updating the root, e.g. changing its children or - // some of its attributes, or replacing the root completely. + // some of its attributes, or replacing the root completely. If the root is + // being updated, update.node_id_to_clear should hold the current root's ID. + // Otherwise if the root is being replaced, update.root_id should hold the ID + // of the new root. bool root_updated = false; - if (update.node_id_to_clear != 0) { - AXNode* node = GetFromId(update.node_id_to_clear); - - // Only destroy the root if the root was replaced and not if it's simply - // updated. To figure out if the root was simply updated, we compare the ID - // of the new root with the existing root ID. - if (node && node == root_) { - if (update.root_id != old_root_id) { - // Clear root_ before calling DestroySubtree so that root_ doesn't ever - // point to an invalid node. - AXNode* old_root = root_; - root_ = nullptr; - DestroySubtree(old_root, &update_state); - } else { - root_updated = true; + if (update.node_id_to_clear != AXNode::kInvalidAXID) { + if (AXNode* cleared_node = GetFromId(update.node_id_to_clear)) { + DCHECK(root_); + if (cleared_node == root_) { + // Only destroy the root if the root was replaced and not if it's simply + // updated. To figure out if the root was simply updated, we compare + // the ID of the new root with the existing root ID. + if (update.root_id != old_root_id) { + // Clear root_ before calling DestroySubtree so that root_ doesn't + // ever point to an invalid node. + AXNode* old_root = root_; + root_ = nullptr; + DestroySubtree(old_root, &update_state); + } else { + // If the root has simply been updated, we treat it like an update to + // any other node. + root_updated = true; + } } - } - // If the root has simply been updated, we treat it like an update to any - // other node. - if (node && root_ && (node != root_ || root_updated)) { - for (auto* child : node->children()) - DestroySubtree(child, &update_state); - std::vector<AXNode*> children; - node->SwapChildren(children); - update_state.pending_nodes.insert(node); + // If the tree doesn't exists any more because the root has just been + // replaced, there is nothing more to clear. + if (root_) { + for (auto* child : cleared_node->children()) + DestroySubtree(child, &update_state); + std::vector<AXNode*> children; + cleared_node->SwapChildren(children); + update_state.pending_nodes.insert(cleared_node->id()); + } } } - bool root_exists = GetFromId(update.root_id) != nullptr; + DCHECK_EQ(!GetFromId(update.root_id), update_state.root_will_be_created); + + // Update the tree data, do not call |UpdateData| since we want to defer + // the |OnTreeDataChanged| event until after the tree has finished updating. + if (update.has_tree_data && data_ != update.tree_data) { + update_state.old_tree_data = data_; + data_ = update.tree_data; + } + + // Update all of the nodes in the update. for (size_t i = 0; i < update.nodes.size(); ++i) { - bool is_new_root = !root_exists && update.nodes[i].id == update.root_id; + const bool is_new_root = update_state.root_will_be_created && + update.nodes[i].id == update.root_id; if (!UpdateNode(update.nodes[i], is_new_root, &update_state)) return false; } @@ -466,12 +886,8 @@ bool AXTree::Unserialize(const AXTreeUpdate& update) { return false; } - if (!update_state.pending_nodes.empty()) { - error_ = "Nodes left pending by the update:"; - for (const AXNode* pending : update_state.pending_nodes) - error_ += base::StringPrintf(" %d", pending->id()); + if (!ValidatePendingChangesComplete(update_state)) return false; - } // Look for changes to nodes that are a descendant of a table, // and invalidate their table info if so. We have to walk up the @@ -495,7 +911,6 @@ bool AXTree::Unserialize(const AXTreeUpdate& update) { // Clear list_info_map_ ordered_set_info_map_.clear(); - std::set<const AXNode*>& new_nodes = update_state.new_nodes; std::vector<AXTreeObserver::Change> changes; changes.reserve(update.nodes.size()); for (size_t i = 0; i < update.nodes.size(); ++i) { @@ -503,7 +918,7 @@ bool AXTree::Unserialize(const AXTreeUpdate& update) { if (!node) continue; - bool is_new_node = update_state.IsNewNode(node); + bool is_new_node = update_state.IsCreatedNode(node); bool is_reparented_node = update_state.IsReparentedNode(node); AXTreeObserver::ChangeType change = AXTreeObserver::NODE_CHANGED; @@ -514,7 +929,7 @@ bool AXTree::Unserialize(const AXTreeUpdate& update) { // Note that we also need to check for the special case when we update // the root without replacing it. bool is_subtree = !node->parent() || - new_nodes.find(node->parent()) == new_nodes.end() || + !update_state.IsCreatedNode(node->parent()) || (node->parent() == root_ && root_updated); change = is_subtree ? AXTreeObserver::SUBTREE_REPARENTED : AXTreeObserver::NODE_REPARENTED; @@ -524,7 +939,7 @@ bool AXTree::Unserialize(const AXTreeUpdate& update) { // Note that we also need to check for the special case when we update // the root without replacing it. bool is_subtree = !node->parent() || - new_nodes.find(node->parent()) == new_nodes.end() || + !update_state.IsCreatedNode(node->parent()) || update_state.IsRemovedNode(node->parent()) || (node->parent() == root_ && root_updated); change = is_subtree ? AXTreeObserver::SUBTREE_CREATED @@ -534,25 +949,58 @@ bool AXTree::Unserialize(const AXTreeUpdate& update) { changes.push_back(AXTreeObserver::Change(node, change)); } - // Update the unignored cached values as necessary. - for (int parent_id : update_state.invalidate_unignored_cached_values_ids) { - AXNode* parent = GetFromId(parent_id); - if (parent) - parent->UpdateUnignoredCachedValues(); + // Update the unignored cached values as necessary, ensuring that we only + // update once for each unignored node. + // If the node is ignored, we must update from an unignored ancestor. + std::set<AXNode::AXID> updated_unignored_cached_values_ids; + for (AXNode::AXID node_id : + update_state.invalidate_unignored_cached_values_ids) { + AXNode* node = GetFromId(node_id); + while (node && node->data().HasState(ax::mojom::State::kIgnored)) + node = node->parent(); + if (node && updated_unignored_cached_values_ids.insert(node->id()).second) + node->UpdateUnignoredCachedValues(); + } + + // Tree is no longer updating. + SetTreeUpdateInProgressState(false); + + // Now that the tree is stable and its nodes have been updated, notify if + // the tree data changed. We must do this after updating nodes in case the + // root has been replaced, so observers have the most up-to-date information. + if (update_state.old_tree_data) { + for (AXTreeObserver& observer : observers_) + observer.OnTreeDataChanged(this, *update_state.old_tree_data, data_); + } + + // Now that the unignored cached values are up to date, update observers to + // new nodes in the tree. + for (AXNode::AXID node_id : update_state.new_node_ids) { + NotifyNodeHasBeenReparentedOrCreated(GetFromId(node_id), &update_state); } // Now that the unignored cached values are up to date, update observers to // node changes. - for (int node_data_changed_id : update_state.node_data_changed_ids) { + for (AXNode::AXID node_data_changed_id : update_state.node_data_changed_ids) { AXNode* node = GetFromId(node_data_changed_id); - if (node) { - for (AXTreeObserver& observer : observers_) - observer.OnNodeChanged(this, node); + DCHECK(node); + + // If the node exists and is in the old data map, then the node data + // may have changed unless this is a new root. + const bool is_new_root = update_state.root_will_be_created && + node_data_changed_id == update.root_id; + if (!is_new_root) { + auto it = update_state.old_node_id_to_data.find(node_data_changed_id); + if (it != update_state.old_node_id_to_data.end()) { + const AXNodeData& old_node_data = it->second; + NotifyNodeDataHasBeenChanged(node, old_node_data, node->data()); + } } - } - // Tree is no longer updating. - SetTreeUpdateInProgressState(false); + // |OnNodeChanged| should be fired for all nodes that have been updated. + for (AXTreeObserver& observer : observers_) + observer.OnNodeChanged(this, node); + } for (AXTreeObserver& observer : observers_) { observer.OnAtomicUpdateFinished(this, root_->id() != old_root_id, changes); @@ -562,6 +1010,7 @@ bool AXTree::Unserialize(const AXTreeUpdate& update) { } AXTableInfo* AXTree::GetTableInfo(const AXNode* const_table_node) const { + DCHECK(!GetTreeUpdateInProgressState()); // Note: the const_casts are here because we want this function to be able // to be called from a const virtual function on AXNode. AXTableInfo is // computed on demand and cached, but that's an implementation detail @@ -607,31 +1056,211 @@ std::string AXTree::ToString() const { } AXNode* AXTree::CreateNode(AXNode* parent, - int32_t id, + AXNode::AXID id, size_t index_in_parent, AXTreeUpdateState* update_state) { + DCHECK(GetTreeUpdateInProgressState()); + // |update_state| must already contain information about all of the expected + // changes and invalidations to apply. If any of these are missing, observers + // may not be notified of changes. + DCHECK(!GetFromId(id)); + DCHECK_GT(update_state->GetPendingCreateNodeCount(id), 0); + DCHECK(update_state->InvalidatesUnignoredCachedValues(id)); + DCHECK(!parent || + update_state->InvalidatesUnignoredCachedValues(parent->id())); + update_state->DecrementPendingCreateNodeCount(id); + update_state->new_node_ids.insert(id); // If this node is the root, use the given index_in_parent as the unignored // index in parent to provide consistency with index_in_parent. AXNode* new_node = new AXNode(this, parent, id, index_in_parent, parent ? 0 : index_in_parent); id_map_[new_node->id()] = new_node; - for (AXTreeObserver& observer : observers_) { - if (update_state->IsReparentedNode(new_node)) - observer.OnNodeReparented(this, new_node); - else - observer.OnNodeCreated(this, new_node); + return new_node; +} + +bool AXTree::ComputePendingChanges(const AXTreeUpdate& update, + AXTreeUpdateState& update_state) { + base::AutoReset<bool> computing_pending_changes_resetter( + &update_state.computing_pending_changes, true); + base::AutoReset<base::Optional<AXNode::AXID>> pending_root_id_resetter( + &update_state.pending_root_id, + root_ ? base::Optional<AXNode::AXID>{root_->id()} : base::nullopt); + + // We distinguish between updating the root, e.g. changing its children or + // some of its attributes, or replacing the root completely. If the root is + // being updated, update.node_id_to_clear should hold the current root's ID. + // Otherwise if the root is being replaced, update.root_id should hold the ID + // of the new root. + if (update.node_id_to_clear != AXNode::kInvalidAXID) { + if (AXNode* cleared_node = GetFromId(update.node_id_to_clear)) { + DCHECK(root_); + if (cleared_node == root_ && + update.root_id != update_state.pending_root_id) { + // Only destroy the root if the root was replaced and not if it's simply + // updated. To figure out if the root was simply updated, we compare + // the ID of the new root with the existing root ID. + MarkSubtreeForDestruction(*update_state.pending_root_id, &update_state); + } + + // If the tree has been marked for destruction because the root will be + // replaced, there is nothing more to clear. + if (update_state.ShouldPendingNodeExistInTree(root_->id())) { + update_state.invalidate_unignored_cached_values_ids.insert( + cleared_node->id()); + update_state.ClearLastKnownPendingNodeData(cleared_node->id()); + for (AXNode* child : cleared_node->children()) { + MarkSubtreeForDestruction(child->id(), &update_state); + } + } + } } - AXNode* unignored_parent = new_node->GetUnignoredParent(); - if (unignored_parent) { - update_state->invalidate_unignored_cached_values_ids.insert( - unignored_parent->id()); + + update_state.root_will_be_created = + !GetFromId(update.root_id) || + !update_state.ShouldPendingNodeExistInTree(update.root_id); + + // Populate |update_state| with all of the changes that will be performed + // on the tree during the update. + for (const AXNodeData& new_data : update.nodes) { + bool is_new_root = + update_state.root_will_be_created && new_data.id == update.root_id; + if (!ComputePendingChangesToNode(new_data, is_new_root, &update_state)) { + return false; + } } - return new_node; + + return true; +} + +bool AXTree::ComputePendingChangesToNode(const AXNodeData& new_data, + bool is_new_root, + AXTreeUpdateState* update_state) { + // If the node does not exist in the tree throw an error unless this + // is the new root and it can be created. + if (!update_state->ShouldPendingNodeExistInTree(new_data.id)) { + if (!is_new_root) { + error_ = base::StringPrintf( + "%d will not be in the tree and is not the new root", new_data.id); + return false; + } + + // Creation is implicit for new root nodes. If |new_data.id| is already + // pending for creation, then it must be a duplicate entry in the tree. + if (!update_state->IncrementPendingCreateNodeCount(new_data.id, + base::nullopt)) { + error_ = base::StringPrintf( + "Node %d is already pending for creation, cannot be the new root", + new_data.id); + return false; + } + if (update_state->pending_root_id) { + MarkSubtreeForDestruction(*update_state->pending_root_id, update_state); + } + update_state->pending_root_id = new_data.id; + } + + // Create a set of new child ids so we can use it to find the nodes that + // have been added and removed. Returns false if a duplicate is found. + std::set<AXNode::AXID> new_child_id_set; + for (AXNode::AXID new_child_id : new_data.child_ids) { + if (base::Contains(new_child_id_set, new_child_id)) { + error_ = base::StringPrintf("Node %d has duplicate child id %d", + new_data.id, new_child_id); + return false; + } + new_child_id_set.insert(new_child_id); + } + + // If the node has not been initialized yet then its node data has either been + // cleared when handling |node_id_to_clear|, or it's a new node. + // In either case, all children must be created. + if (update_state->DoesPendingNodeRequireInit(new_data.id)) { + update_state->invalidate_unignored_cached_values_ids.insert(new_data.id); + + // If this node has been cleared via |node_id_to_clear| or is a new node, + // the last-known parent's unignored cache needs to be updated. + update_state->InvalidateParentNodeUnignoredCacheValues(new_data.id); + + for (AXNode::AXID child_id : new_child_id_set) { + // If a |child_id| is already pending for creation, then it must be a + // duplicate entry in the tree. + update_state->invalidate_unignored_cached_values_ids.insert(child_id); + if (!update_state->IncrementPendingCreateNodeCount(child_id, + new_data.id)) { + error_ = base::StringPrintf( + "Node %d is already pending for creation, cannot be a new child", + child_id); + return false; + } + } + + update_state->SetLastKnownPendingNodeData(&new_data); + return true; + } + + const AXNodeData& old_data = + update_state->GetLastKnownPendingNodeData(new_data.id); + + // Create a set of old child ids so we can use it to find the nodes that + // have been added and removed. + std::set<AXNode::AXID> old_child_id_set(old_data.child_ids.cbegin(), + old_data.child_ids.cend()); + + std::vector<AXNode::AXID> create_or_destroy_ids; + std::set_symmetric_difference( + old_child_id_set.cbegin(), old_child_id_set.cend(), + new_child_id_set.cbegin(), new_child_id_set.cend(), + std::back_inserter(create_or_destroy_ids)); + + // If the node has changed ignored state or there are any differences in + // its children, then its unignored cached values must be invalidated. + const bool ignored_changed = old_data.HasState(ax::mojom::State::kIgnored) != + new_data.HasState(ax::mojom::State::kIgnored); + if (!create_or_destroy_ids.empty() || ignored_changed) { + update_state->invalidate_unignored_cached_values_ids.insert(new_data.id); + + // If this ignored state had changed also invalidate the parent. + update_state->InvalidateParentNodeUnignoredCacheValues(new_data.id); + } + + for (AXNode::AXID child_id : create_or_destroy_ids) { + if (base::Contains(new_child_id_set, child_id)) { + // This is a serious error - nodes should never be reparented without + // first being removed from the tree. If a node exists in the tree already + // then adding it to a new parent would mean stealing the node from its + // old parent which hadn't been updated to reflect the change. + if (update_state->ShouldPendingNodeExistInTree(child_id)) { + error_ = base::StringPrintf( + "Node %d is not marked for destruction, would be reparented to %d", + child_id, new_data.id); + return false; + } + + // If a |child_id| is already pending for creation, then it must be a + // duplicate entry in the tree. + update_state->invalidate_unignored_cached_values_ids.insert(child_id); + if (!update_state->IncrementPendingCreateNodeCount(child_id, + new_data.id)) { + error_ = base::StringPrintf( + "Node %d is already pending for creation, cannot be a new child", + child_id); + return false; + } + } else { + // If |child_id| does not exist in the new set, then it has + // been removed from |node|, and the subtree must be deleted. + MarkSubtreeForDestruction(child_id, update_state); + } + } + + update_state->SetLastKnownPendingNodeData(&new_data); + return true; } bool AXTree::UpdateNode(const AXNodeData& src, bool is_new_root, AXTreeUpdateState* update_state) { + DCHECK(GetTreeUpdateInProgressState()); // This method updates one node in the tree based on serialized data // received in an AXTreeUpdate. See AXTreeUpdate for pre and post // conditions. @@ -641,51 +1270,13 @@ bool AXTree::UpdateNode(const AXNodeData& src, // and this is a serious error. AXNode* node = GetFromId(src.id); if (node) { - update_state->pending_nodes.erase(node); - - // TODO(accessibility): CallNodeChangeCallbacks should not pass |node|, - // since the tree and the node data are not yet in a consistent - // state. Possibly only pass id. - if (!update_state->IsNewNode(node) || + update_state->pending_nodes.erase(node->id()); + UpdateReverseRelations(node, src); + if (!update_state->IsCreatedNode(node) || update_state->IsReparentedNode(node)) { - auto it = update_state->reparented_node_id_to_data.find(node->id()); - const AXNodeData& old_data = - it != update_state->reparented_node_id_to_data.end() ? it->second - : node->data(); - CallNodeChangeCallbacks(node, old_data, src); - if (old_data.HasState(ax::mojom::State::kIgnored) != - src.HasState(ax::mojom::State::kIgnored)) { - AXNode* unignored_parent = node->GetUnignoredParent(); - if (unignored_parent) { - update_state->invalidate_unignored_cached_values_ids.insert( - unignored_parent->id()); - } - - // We must invalidate the node if it's no longer State::kIgnored. - // Since nodes updated in no particular order, this node may - // not be added to the set later or update its cached values. - // - // For example, given the following tree, : - // A unignored - // | - // B ignored - // | - // C unignored - // - // ... and the following updates : - // Update C unignored => ignored - // Update B ignored => unignored - // - // Both updates would add A to the set of nodes which have invalid - // cached values, but B would never be added because ignored nodes - // are skipped over. - if (!src.HasState(ax::mojom::State::kIgnored)) { - update_state->invalidate_unignored_cached_values_ids.insert( - node->id()); - } - } + update_state->old_node_id_to_data.insert( + std::make_pair(node->id(), node->TakeData())); } - UpdateReverseRelations(node, src); node->SetData(src); } else { if (!is_new_root) { @@ -694,9 +1285,7 @@ bool AXTree::UpdateNode(const AXNodeData& src, return false; } - update_state->new_root = CreateNode(nullptr, src.id, 0, update_state); - node = update_state->new_root; - update_state->new_nodes.insert(node); + node = CreateNode(nullptr, src.id, 0, update_state); UpdateReverseRelations(node, src); node->SetData(src); } @@ -705,26 +1294,7 @@ bool AXTree::UpdateNode(const AXNodeData& src, // First, delete nodes that used to be children of this node but aren't // anymore. - if (!DeleteOldChildren(node, src.child_ids, update_state)) { - // If DeleteOldChildren failed, we need to carefully clean up before - // returning false as well. In particular, if this node was a new root, - // we need to safely destroy the whole tree. - if (update_state->new_root) { - AXNode* old_root = root_; - root_ = nullptr; - - DestroySubtree(old_root, update_state); - - // Delete |node|'s subtree too as long as it wasn't already removed - // or added elsewhere in the tree. - if (update_state->removed_node_ids.find(src.id) == - update_state->removed_node_ids.end() && - update_state->new_nodes.find(node) != update_state->new_nodes.end()) { - DestroySubtree(node, update_state); - } - } - return false; - } + DeleteOldChildren(node, src.child_ids, update_state); // Now build a new children vector, reusing nodes when possible, // and swap it in. @@ -746,11 +1316,84 @@ bool AXTree::UpdateNode(const AXNodeData& src, return success; } -void AXTree::CallNodeChangeCallbacks(AXNode* node, - const AXNodeData& old_data, - const AXNodeData& new_data) { +void AXTree::NotifySubtreeWillBeReparentedOrDeleted( + AXNode* node, + const AXTreeUpdateState* update_state) { + DCHECK(!GetTreeUpdateInProgressState()); + if (node->id() == AXNode::kInvalidAXID) + return; + + for (AXTreeObserver& observer : observers_) { + if (update_state->IsPotentiallyReparentedNode(node)) { + observer.OnSubtreeWillBeReparented(this, node); + } else { + observer.OnSubtreeWillBeDeleted(this, node); + } + } +} + +void AXTree::NotifyNodeWillBeReparentedOrDeleted( + AXNode* node, + const AXTreeUpdateState* update_state) { + DCHECK(!GetTreeUpdateInProgressState()); + if (node->id() == AXNode::kInvalidAXID) + return; + + for (AXTreeObserver& observer : observers_) { + if (update_state->IsPotentiallyReparentedNode(node)) { + observer.OnNodeWillBeReparented(this, node); + } else { + observer.OnNodeWillBeDeleted(this, node); + } + } +} + +void AXTree::RecursivelyNotifyNodeWillBeDeleted(AXNode* node) { + DCHECK(!GetTreeUpdateInProgressState()); + if (node->id() == AXNode::kInvalidAXID) + return; + + for (AXTreeObserver& observer : observers_) + observer.OnNodeWillBeDeleted(this, node); + for (auto* child : node->children()) + RecursivelyNotifyNodeWillBeDeleted(child); +} + +void AXTree::NotifyNodeHasBeenReparentedOrCreated( + AXNode* node, + const AXTreeUpdateState* update_state) { + DCHECK(!GetTreeUpdateInProgressState()); + if (node->id() == AXNode::kInvalidAXID) + return; + + for (AXTreeObserver& observer : observers_) { + if (update_state->IsReparentedNode(node)) { + observer.OnNodeReparented(this, node); + } else { + observer.OnNodeCreated(this, node); + } + } +} + +void AXTree::NotifyNodeDataWillChange(const AXNodeData& old_data, + const AXNodeData& new_data) { + DCHECK(!GetTreeUpdateInProgressState()); + if (new_data.id == AXNode::kInvalidAXID) + return; + for (AXTreeObserver& observer : observers_) observer.OnNodeDataWillChange(this, old_data, new_data); +} + +void AXTree::NotifyNodeDataHasBeenChanged(AXNode* node, + const AXNodeData& old_data, + const AXNodeData& new_data) { + DCHECK(!GetTreeUpdateInProgressState()); + if (node->id() == AXNode::kInvalidAXID) + return; + + for (AXTreeObserver& observer : observers_) + observer.OnNodeDataChanged(this, old_data, new_data); if (old_data.role != new_data.role) { for (AXTreeObserver& observer : observers_) @@ -832,6 +1475,7 @@ void AXTree::CallNodeChangeCallbacks(AXNode* node, } void AXTree::UpdateReverseRelations(AXNode* node, const AXNodeData& new_data) { + DCHECK(GetTreeUpdateInProgressState()); const AXNodeData& old_data = node->data(); int id = new_data.id; auto int_callback = [this, id](ax::mojom::IntAttribute attr, @@ -905,20 +1549,89 @@ void AXTree::UpdateReverseRelations(AXNode* node, const AXNodeData& new_data) { string_callback); } +bool AXTree::ValidatePendingChangesComplete( + const AXTreeUpdateState& update_state) { + if (!update_state.pending_nodes.empty()) { + error_ = "Nodes left pending by the update:"; + for (const AXNode::AXID pending_id : update_state.pending_nodes) + error_ += base::StringPrintf(" %d", pending_id); + return false; + } + + if (!update_state.node_id_to_pending_data.empty()) { + std::string destroy_subtree_ids; + std::string destroy_node_ids; + std::string create_node_ids; + + bool has_pending_changes = false; + for (auto&& pair : update_state.node_id_to_pending_data) { + const AXNode::AXID pending_id = pair.first; + const std::unique_ptr<PendingStructureChanges>& data = pair.second; + if (data->DoesNodeExpectAnyStructureChanges()) { + if (data->DoesNodeExpectSubtreeWillBeDestroyed()) + destroy_subtree_ids += base::StringPrintf(" %d", pending_id); + if (data->DoesNodeExpectNodeWillBeDestroyed()) + destroy_node_ids += base::StringPrintf(" %d", pending_id); + if (data->DoesNodeExpectNodeWillBeCreated()) + create_node_ids += base::StringPrintf(" %d", pending_id); + has_pending_changes = true; + } + } + if (has_pending_changes) { + error_ = base::StringPrintf( + "Changes left pending by the update; " + "destroy subtrees: %s, destroy nodes: %s, create nodes: %s", + destroy_subtree_ids.c_str(), destroy_node_ids.c_str(), + create_node_ids.c_str()); + } + return !has_pending_changes; + } + + return true; +} + +void AXTree::MarkSubtreeForDestruction(AXNode::AXID node_id, + AXTreeUpdateState* update_state) { + update_state->IncrementPendingDestroySubtreeCount(node_id); + MarkNodesForDestructionRecursive(node_id, update_state); +} + +void AXTree::MarkNodesForDestructionRecursive(AXNode::AXID node_id, + AXTreeUpdateState* update_state) { + // If this subtree has already been marked for destruction, return so + // we don't walk it again. + if (!update_state->ShouldPendingNodeExistInTree(node_id)) + return; + + const AXNodeData& last_known_data = + update_state->GetLastKnownPendingNodeData(node_id); + + update_state->IncrementPendingDestroyNodeCount(node_id); + for (AXNode::AXID child_id : last_known_data.child_ids) { + MarkNodesForDestructionRecursive(child_id, update_state); + } +} + void AXTree::DestroySubtree(AXNode* node, AXTreeUpdateState* update_state) { + DCHECK(GetTreeUpdateInProgressState()); + // |update_state| must already contain information about all of the expected + // changes and invalidations to apply. If any of these are missing, observers + // may not be notified of changes. DCHECK(update_state); - for (AXTreeObserver& observer : observers_) { - if (update_state->IsPotentiallyReparentedNode(node)) - observer.OnSubtreeWillBeReparented(this, node); - else - observer.OnSubtreeWillBeDeleted(this, node); - } + DCHECK_GT(update_state->GetPendingDestroySubtreeCount(node->id()), 0); + DCHECK(!node->parent() || + update_state->InvalidatesUnignoredCachedValues(node->parent()->id())); + update_state->DecrementPendingDestroySubtreeCount(node->id()); DestroyNodeAndSubtree(node, update_state); } void AXTree::DestroyNodeAndSubtree(AXNode* node, AXTreeUpdateState* update_state) { + DCHECK(GetTreeUpdateInProgressState()); + DCHECK(!update_state || + update_state->GetPendingDestroyNodeCount(node->id()) > 0); + // Clear out any reverse relations. AXNodeData empty_data; empty_data.id = node->id(); @@ -931,62 +1644,46 @@ void AXTree::DestroyNodeAndSubtree(AXNode* node, table_info_map_.erase(node->id()); } - for (AXTreeObserver& observer : observers_) { - if (update_state && update_state->IsPotentiallyReparentedNode(node)) - observer.OnNodeWillBeReparented(this, node); - else - observer.OnNodeWillBeDeleted(this, node); - } id_map_.erase(node->id()); for (auto* child : node->children()) DestroyNodeAndSubtree(child, update_state); if (update_state) { - AXNode* unignored_parent = node->GetUnignoredParent(); - if (unignored_parent) { - update_state->invalidate_unignored_cached_values_ids.insert( - unignored_parent->id()); - } - update_state->pending_nodes.erase(node); + update_state->pending_nodes.erase(node->id()); + update_state->DecrementPendingDestroyNodeCount(node->id()); update_state->removed_node_ids.insert(node->id()); - } - - if (update_state && update_state->IsReparentedNode(node)) { - update_state->reparented_node_id_to_data.insert( - std::make_pair(node->id(), node->TakeData())); + update_state->new_node_ids.erase(node->id()); + update_state->node_data_changed_ids.erase(node->id()); + if (update_state->IsReparentedNode(node)) { + update_state->old_node_id_to_data.emplace( + std::make_pair(node->id(), node->TakeData())); + } } node->Destroy(); } -bool AXTree::DeleteOldChildren(AXNode* node, +void AXTree::DeleteOldChildren(AXNode* node, const std::vector<int32_t>& new_child_ids, AXTreeUpdateState* update_state) { - // Create a set of child ids in |src| for fast lookup, and return false - // if a duplicate is found; - std::set<int32_t> new_child_id_set; - for (size_t i = 0; i < new_child_ids.size(); ++i) { - if (new_child_id_set.find(new_child_ids[i]) != new_child_id_set.end()) { - error_ = base::StringPrintf("Node %d has duplicate child id %d", - node->id(), new_child_ids[i]); - return false; - } - new_child_id_set.insert(new_child_ids[i]); - } + DCHECK(GetTreeUpdateInProgressState()); + // Create a set of child ids in |src| for fast lookup, we know the set does + // not contain duplicate entries already, because that was handled when + // populating |update_state| with information about all of the expected + // changes to be applied. + std::set<int32_t> new_child_id_set(new_child_ids.begin(), + new_child_ids.end()); // Delete the old children. - const std::vector<AXNode*>& old_children = node->children(); - for (size_t i = 0; i < old_children.size(); ++i) { - int old_id = old_children[i]->id(); - if (new_child_id_set.find(old_id) == new_child_id_set.end()) - DestroySubtree(old_children[i], update_state); + for (AXNode* child : node->children()) { + if (!base::Contains(new_child_id_set, child->id())) + DestroySubtree(child, update_state); } - - return true; } bool AXTree::CreateNewChildVector(AXNode* node, const std::vector<int32_t>& new_child_ids, std::vector<AXNode*>* new_children, AXTreeUpdateState* update_state) { + DCHECK(GetTreeUpdateInProgressState()); bool success = true; for (size_t i = 0; i < new_child_ids.size(); ++i) { int32_t child_id = new_child_ids[i]; @@ -1007,8 +1704,7 @@ bool AXTree::CreateNewChildVector(AXNode* node, child->SetIndexInParent(i); } else { child = CreateNode(node, child_id, i, update_state); - update_state->pending_nodes.insert(child); - update_state->new_nodes.insert(child); + update_state->pending_nodes.insert(child->id()); } new_children->push_back(child); } @@ -1037,8 +1733,8 @@ void AXTree::PopulateOrderedSetItems(const AXNode* ordered_set, const AXNode* local_parent, std::vector<const AXNode*>& items, const AXNode& original_node) const { - // ignored nodes are not a part of ordered sets. - if (original_node.data().HasState(ax::mojom::State::kIgnored)) + // Ignored nodes are not a part of ordered sets. + if (original_node.IsIgnored()) return; // Stop searching current path if roles of local_parent and ordered set match. diff --git a/chromium/ui/accessibility/ax_tree.h b/chromium/ui/accessibility/ax_tree.h index 5a657d2e30f..fc27e059651 100644 --- a/chromium/ui/accessibility/ax_tree.h +++ b/chromium/ui/accessibility/ax_tree.h @@ -173,25 +173,78 @@ class AX_EXPORT AXTree : public AXNode::OwnerTree { AXTableInfo* GetTableInfo(const AXNode* table_node) const override; AXNode* CreateNode(AXNode* parent, - int32_t id, + AXNode::AXID id, size_t index_in_parent, AXTreeUpdateState* update_state); + // Accumulates the work that will be required to update the AXTree. + // This allows us to notify observers of structure changes when the + // tree is still in a stable and unchanged state. + bool ComputePendingChanges(const AXTreeUpdate& update, + AXTreeUpdateState& update_state); + + // Populates |update_state| with information about actions that will + // be performed on the tree during the update, such as adding or + // removing nodes in the tree. Returns true on success. + // Nothing within this call should modify tree structure or node data. + bool ComputePendingChangesToNode(const AXNodeData& new_data, + bool is_new_root, + AXTreeUpdateState* update_state); + // This is called from within Unserialize(), it returns true on success. bool UpdateNode(const AXNodeData& src, bool is_new_root, AXTreeUpdateState* update_state); - void CallNodeChangeCallbacks(AXNode* node, - const AXNodeData& old_data, - const AXNodeData& new_data); + // Notify the delegate that the subtree rooted at |node| will be + // destroyed or reparented. + void NotifySubtreeWillBeReparentedOrDeleted( + AXNode* node, + const AXTreeUpdateState* update_state); + + // Notify the delegate that |node| will be destroyed or reparented. + void NotifyNodeWillBeReparentedOrDeleted( + AXNode* node, + const AXTreeUpdateState* update_state); + + // Notify the delegate that |node| and all of its descendants will be + // destroyed. + void RecursivelyNotifyNodeWillBeDeleted(AXNode* node); + + // Notify the delegate that |node| has been created or reparented. + void NotifyNodeHasBeenReparentedOrCreated( + AXNode* node, + const AXTreeUpdateState* update_state); + + // Notify the delegate that a node will change its data. + void NotifyNodeDataWillChange(const AXNodeData& old_data, + const AXNodeData& new_data); + + // Notify the delegate that |node| has changed its data. + void NotifyNodeDataHasBeenChanged(AXNode* node, + const AXNodeData& old_data, + const AXNodeData& new_data); void UpdateReverseRelations(AXNode* node, const AXNodeData& new_data); - void OnRootChanged(); + // Returns true if all pending changes in the |update_state| have been + // handled. If this returns false, the |error_| message will be populated. + // It's a fatal error to have pending changes after exhausting + // the AXTreeUpdate. + bool ValidatePendingChangesComplete(const AXTreeUpdateState& update_state); + + // Modifies |update_state| so that it knows what subtree and nodes are + // going to be destroyed for the subtree rooted at |node|. + void MarkSubtreeForDestruction(AXNode::AXID node_id, + AXTreeUpdateState* update_state); + + // Modifies |update_state| so that it knows what nodes are + // going to be destroyed for the subtree rooted at |node|. + void MarkNodesForDestructionRecursive(AXNode::AXID node_id, + AXTreeUpdateState* update_state); - // Notify the delegate that the subtree rooted at |node| will be destroyed, - // then call DestroyNodeAndSubtree on it. + // Validates that destroying the subtree rooted at |node| has required + // information in |update_state|, then calls DestroyNodeAndSubtree on it. void DestroySubtree(AXNode* node, AXTreeUpdateState* update_state); // Call Destroy() on |node|, and delete it from the id map, and then @@ -199,9 +252,8 @@ class AX_EXPORT AXTree : public AXNode::OwnerTree { void DestroyNodeAndSubtree(AXNode* node, AXTreeUpdateState* update_state); // Iterate over the children of |node| and for each child, destroy the - // child and its subtree if its id is not in |new_child_ids|. Returns - // true on success, false on fatal error. - bool DeleteOldChildren(AXNode* node, + // child and its subtree if its id is not in |new_child_ids|. + void DeleteOldChildren(AXNode* node, const std::vector<int32_t>& new_child_ids, AXTreeUpdateState* update_state); diff --git a/chromium/ui/accessibility/ax_tree_observer.h b/chromium/ui/accessibility/ax_tree_observer.h index eec7d75498b..d6652aca47c 100644 --- a/chromium/ui/accessibility/ax_tree_observer.h +++ b/chromium/ui/accessibility/ax_tree_observer.h @@ -18,33 +18,34 @@ struct AXTreeData; // Used when you want to be notified when changes happen to an AXTree. // -// Some of the notifications are called in the middle of an update operation. -// Be careful, as the tree may be in an inconsistent state at this time; -// don't walk the parents and children at this time: -// OnNodeWillBeDeleted -// OnSubtreeWillBeDeleted -// OnNodeWillBeReparented -// OnSubtreeWillBeReparented -// OnNodeCreated -// OnNodeReparented -// OnNodeChanged -// -// In addition, one additional notification is fired at the end of an -// atomic update, and it provides a vector of nodes that were added or -// changed, for final postprocessing: -// OnAtomicUpdateFinished -// +// |OnAtomicUpdateFinished| is notified at the end of an atomic update. +// It provides a vector of nodes that were added or changed, for final +// postprocessing. class AX_EXPORT AXTreeObserver : public base::CheckedObserver { public: AXTreeObserver(); ~AXTreeObserver() override; - // Called before a node's data gets updated. + // Called before any tree modifications have occurred, notifying that a single + // node will change its data. Its id and data will be valid, but its links to + // parents and children are only valid within this callstack. Do not hold a + // reference to the node or any relative nodes such as ancestors or + // descendants described by the node data outside of this event. virtual void OnNodeDataWillChange(AXTree* tree, const AXNodeData& old_node_data, const AXNodeData& new_node_data) {} + // Called after all tree modifications have occurred, notifying that a single + // node has changed its data. Its id, data, and links to parent and children + // will all be valid, since the tree is in a stable state after updating. + virtual void OnNodeDataChanged(AXTree* tree, + const AXNodeData& old_node_data, + const AXNodeData& new_node_data) {} + // Individual callbacks for every attribute of AXNodeData that can change. + // Called after all tree mutations have occurred, notifying that a single node + // changed its data. Its id, data, and links to parent and children will all + // be valid, since the tree is in a stable state after updating. virtual void OnRoleChanged(AXTree* tree, AXNode* node, ax::mojom::Role old_role, @@ -85,19 +86,18 @@ class AX_EXPORT AXTreeObserver : public base::CheckedObserver { const std::vector<std::string>& old_value, const std::vector<std::string>& new_value) {} - // Called when tree data changes. + // Called when tree data changes, after all nodes have been updated. virtual void OnTreeDataChanged(AXTree* tree, const AXTreeData& old_data, const AXTreeData& new_data) {} - // Called just before a node is deleted. Its id and data will be valid, - // but its links to parents and children are invalid. This is called - // in the middle of an update, the tree may be in an invalid state! + // Called before any tree modifications have occurred, notifying that a single + // node will be deleted. Its id and data will be valid, but its links to + // parents and children are only valid within this callstack. Do not hold + // a reference to node outside of the event. virtual void OnNodeWillBeDeleted(AXTree* tree, AXNode* node) {} // Same as OnNodeWillBeDeleted, but only called once for an entire subtree. - // This is called in the middle of an update, the tree may be in an - // invalid state! virtual void OnSubtreeWillBeDeleted(AXTree* tree, AXNode* node) {} // Called just before a node is deleted for reparenting. See @@ -108,16 +108,18 @@ class AX_EXPORT AXTreeObserver : public base::CheckedObserver { // |OnSubtreeWillBeDeleted| for additional information. virtual void OnSubtreeWillBeReparented(AXTree* tree, AXNode* node) {} - // Called immediately after a new node is created. The tree may be in - // the middle of an update, don't walk the parents and children now. + // Called after all tree mutations have occurred, notifying that a single node + // has been created. Its id, data, and links to parent and children will all + // be valid, since the tree is in a stable state after updating. virtual void OnNodeCreated(AXTree* tree, AXNode* node) {} - // Called immediately after a node is reparented. The tree may be in the - // middle of an update, don't walk the parents and children now. + // Same as |OnNodeCreated|, but called for nodes that have been reparented. virtual void OnNodeReparented(AXTree* tree, AXNode* node) {} - // Called when a node changes its data or children. The tree may be in - // the middle of an update, don't walk the parents and children now. + // Called after all tree mutations have occurred, notifying that a single node + // has updated its data or children. Its id, data, and links to parent and + // children will all be valid, since the tree is in a stable state after + // updating. virtual void OnNodeChanged(AXTree* tree, AXNode* node) {} enum ChangeType { diff --git a/chromium/ui/accessibility/ax_tree_unittest.cc b/chromium/ui/accessibility/ax_tree_unittest.cc index 9dd7309d9f3..63e3bb32389 100644 --- a/chromium/ui/accessibility/ax_tree_unittest.cc +++ b/chromium/ui/accessibility/ax_tree_unittest.cc @@ -84,13 +84,23 @@ class TestAXTreeObserver : public AXTreeObserver { void OnNodeDataWillChange(AXTree* tree, const AXNodeData& old_node_data, const AXNodeData& new_node_data) override {} + void OnNodeDataChanged(AXTree* tree, + const AXNodeData& old_node_data, + const AXNodeData& new_node_data) override {} void OnTreeDataChanged(AXTree* tree, const ui::AXTreeData& old_data, const ui::AXTreeData& new_data) override { tree_data_changed_ = true; } + + base::Optional<AXNode::AXID> unignored_parent_id_before_node_deleted; void OnNodeWillBeDeleted(AXTree* tree, AXNode* node) override { deleted_ids_.push_back(node->id()); + if (unignored_parent_id_before_node_deleted) { + ASSERT_NE(nullptr, node->GetUnignoredParent()); + ASSERT_EQ(*unignored_parent_id_before_node_deleted, + node->GetUnignoredParent()->id()); + } } void OnSubtreeWillBeDeleted(AXTree* tree, AXNode* node) override { @@ -115,8 +125,6 @@ class TestAXTreeObserver : public AXTreeObserver { void OnNodeChanged(AXTree* tree, AXNode* node) override { changed_ids_.push_back(node->id()); - if (call_posinset_and_setsize) - AssertPosinsetAndSetsizeNotSet(node); } void OnAtomicUpdateFinished(AXTree* tree, @@ -247,12 +255,6 @@ class TestAXTreeObserver : public AXTreeObserver { return attribute_change_log_; } - bool call_posinset_and_setsize = false; - void AssertPosinsetAndSetsizeNotSet(AXNode* node) { - ASSERT_FALSE(node->GetPosInSet()); - ASSERT_FALSE(node->GetSetSize()); - } - private: AXTree* tree_; bool tree_data_changed_; @@ -457,7 +459,8 @@ TEST(AXTreeTest, InvalidReparentingFails) { update.nodes[1].id = 2; update.nodes[2].id = 3; EXPECT_FALSE(tree.Unserialize(update)); - ASSERT_EQ("Node 3 reparented from 2 to 1", tree.error()); + ASSERT_EQ("Node 3 is not marked for destruction, would be reparented to 1", + tree.error()); } TEST(AXTreeTest, NoReparentingOfRootIfNoNewRoot) { @@ -814,6 +817,35 @@ TEST(AXTreeTest, MultipleIgnoredChangesDoesNotBreakCache) { EXPECT_TRUE(tree.GetFromId(3)->data().HasState(ax::mojom::State::kIgnored)); } +TEST(AXTreeTest, NodeToClearUpdatesParentUnignoredCount) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(4); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].child_ids.push_back(2); + initial_state.nodes[1].id = 2; + initial_state.nodes[1].AddState(ax::mojom::State::kIgnored); + initial_state.nodes[1].child_ids.push_back(3); + initial_state.nodes[1].child_ids.push_back(4); + initial_state.nodes[2].id = 3; + initial_state.nodes[3].id = 4; + + AXTree tree(initial_state); + EXPECT_EQ(2u, tree.GetFromId(1)->GetUnignoredChildCount()); + EXPECT_EQ(2u, tree.GetFromId(2)->GetUnignoredChildCount()); + + AXTreeUpdate update; + update.nodes.resize(1); + update.node_id_to_clear = 2; + update.root_id = 1; + update.nodes[0] = initial_state.nodes[1]; + update.nodes[0].state = 0; + update.nodes[0].child_ids.resize(0); + EXPECT_TRUE(tree.Unserialize(update)) << tree.error(); + + EXPECT_EQ(1u, tree.GetFromId(1)->GetUnignoredChildCount()); +} + TEST(AXTreeTest, TreeObserverIsNotCalledForReparenting) { AXTreeUpdate initial_state; initial_state.root_id = 1; @@ -1629,6 +1661,7 @@ TEST(AXTreeTest, CachedUnignoredValues) { root = tree.root(); EXPECT_EQ(2u, root->GetUnignoredChildCount()); EXPECT_EQ(2, root->GetUnignoredChildAtIndex(0)->id()); + EXPECT_EQ(2u, tree.GetFromId(2)->GetUnignoredChildCount()); EXPECT_EQ(0u, tree.GetFromId(4)->GetUnignoredIndexInParent()); EXPECT_EQ(1u, tree.GetFromId(5)->GetUnignoredIndexInParent()); @@ -1639,6 +1672,7 @@ TEST(AXTreeTest, CachedUnignoredValues) { EXPECT_TRUE(tree.Unserialize(update2)); + EXPECT_EQ(1u, tree.GetFromId(2)->GetUnignoredChildCount()); EXPECT_EQ(0u, tree.GetFromId(5)->GetUnignoredIndexInParent()); // Ensure siblings of a deleted node are updated. @@ -1649,6 +1683,7 @@ TEST(AXTreeTest, CachedUnignoredValues) { EXPECT_TRUE(tree.Unserialize(update3)); + EXPECT_EQ(1u, tree.GetFromId(1)->GetUnignoredChildCount()); EXPECT_EQ(0u, tree.GetFromId(3)->GetUnignoredIndexInParent()); // Ensure new nodes are correctly updated. @@ -1662,6 +1697,7 @@ TEST(AXTreeTest, CachedUnignoredValues) { EXPECT_TRUE(tree.Unserialize(update4)); + EXPECT_EQ(2u, tree.GetFromId(1)->GetUnignoredChildCount()); EXPECT_EQ(0u, tree.GetFromId(3)->GetUnignoredIndexInParent()); EXPECT_EQ(1u, tree.GetFromId(6)->GetUnignoredIndexInParent()); EXPECT_EQ(0u, tree.GetFromId(7)->GetUnignoredIndexInParent()); @@ -1750,111 +1786,111 @@ TEST(AXTreeTest, NullUnignoredChildren) { TEST(AXTreeTest, UnignoredChildIterator) { AXTreeUpdate tree_update; // (i) => node is ignored - // 0 + // 1 // |__________ // | | | - // 1(i) 2 3 + // 2(i) 3 4 // |_______________________ // | | | | - // 4 5 6(i) 7(i) + // 5 6 7(i) 8(i) // | | |________ // | | | | - // 8 9(i) 10(i) 11 + // 9 10(i) 11(i) 12 // | |____ // | | | - // 12(i) 13 14 - tree_update.root_id = 0; + // 13(i) 14 15 + tree_update.root_id = 1; tree_update.nodes.resize(15); - tree_update.nodes[0].id = 0; - tree_update.nodes[0].child_ids = {1, 2, 3}; + tree_update.nodes[0].id = 1; + tree_update.nodes[0].child_ids = {2, 3, 4}; - tree_update.nodes[1].id = 1; - tree_update.nodes[1].child_ids = {4, 5, 6, 7}; + tree_update.nodes[1].id = 2; + tree_update.nodes[1].child_ids = {5, 6, 7, 8}; tree_update.nodes[1].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[2].id = 2; - tree_update.nodes[3].id = 3; + tree_update.nodes[2].id = 3; + tree_update.nodes[3].id = 4; - tree_update.nodes[4].id = 4; - tree_update.nodes[4].child_ids = {8}; + tree_update.nodes[4].id = 5; + tree_update.nodes[4].child_ids = {9}; - tree_update.nodes[5].id = 5; - tree_update.nodes[5].child_ids = {9}; + tree_update.nodes[5].id = 6; + tree_update.nodes[5].child_ids = {10}; - tree_update.nodes[6].id = 6; - tree_update.nodes[6].child_ids = {10, 11}; + tree_update.nodes[6].id = 7; + tree_update.nodes[6].child_ids = {11, 12}; tree_update.nodes[6].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[7].id = 7; + tree_update.nodes[7].id = 8; tree_update.nodes[7].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[8].id = 8; + tree_update.nodes[8].id = 9; - tree_update.nodes[9].id = 9; - tree_update.nodes[9].child_ids = {12}; + tree_update.nodes[9].id = 10; + tree_update.nodes[9].child_ids = {13}; tree_update.nodes[9].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[10].id = 10; - tree_update.nodes[10].child_ids = {13, 14}; + tree_update.nodes[10].id = 11; + tree_update.nodes[10].child_ids = {14, 15}; tree_update.nodes[10].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[11].id = 11; + tree_update.nodes[11].id = 12; - tree_update.nodes[12].id = 12; + tree_update.nodes[12].id = 13; tree_update.nodes[12].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[13].id = 13; + tree_update.nodes[13].id = 14; - tree_update.nodes[14].id = 14; + tree_update.nodes[14].id = 15; AXTree tree(tree_update); AXNode* root = tree.root(); // Test traversal - // UnignoredChildren(root) = {4, 5, 13, 14, 11, 2, 3} + // UnignoredChildren(root) = {5, 6, 14, 15, 12, 3, 4} AXNode::UnignoredChildIterator unignored_iterator = root->UnignoredChildrenBegin(); - EXPECT_EQ(4, (*unignored_iterator).id()); - - EXPECT_EQ(5, (*++unignored_iterator).id()); + EXPECT_EQ(5, (*unignored_iterator).id()); - EXPECT_EQ(13, (*++unignored_iterator).id()); + EXPECT_EQ(6, (*++unignored_iterator).id()); EXPECT_EQ(14, (*++unignored_iterator).id()); - EXPECT_EQ(13, (*--unignored_iterator).id()); + EXPECT_EQ(15, (*++unignored_iterator).id()); - EXPECT_EQ(5, (*--unignored_iterator).id()); + EXPECT_EQ(14, (*--unignored_iterator).id()); - EXPECT_EQ(13, (*++unignored_iterator).id()); + EXPECT_EQ(6, (*--unignored_iterator).id()); EXPECT_EQ(14, (*++unignored_iterator).id()); - EXPECT_EQ(11, (*++unignored_iterator).id()); + EXPECT_EQ(15, (*++unignored_iterator).id()); - EXPECT_EQ(2, (*++unignored_iterator).id()); + EXPECT_EQ(12, (*++unignored_iterator).id()); EXPECT_EQ(3, (*++unignored_iterator).id()); + EXPECT_EQ(4, (*++unignored_iterator).id()); + EXPECT_EQ(root->UnignoredChildrenEnd(), ++unignored_iterator); // test empty list - // UnignoredChildren(2) = {} - AXNode* node2 = tree.GetFromId(2); - unignored_iterator = node2->UnignoredChildrenBegin(); - EXPECT_EQ(node2->UnignoredChildrenEnd(), unignored_iterator); + // UnignoredChildren(3) = {} + AXNode* node3 = tree.GetFromId(3); + unignored_iterator = node3->UnignoredChildrenBegin(); + EXPECT_EQ(node3->UnignoredChildrenEnd(), unignored_iterator); // empty list from ignored node with no children - // UnignoredChildren(7) = {} - AXNode* node7 = tree.GetFromId(7); - unignored_iterator = node7->UnignoredChildrenBegin(); - EXPECT_EQ(node7->UnignoredChildrenEnd(), unignored_iterator); + // UnignoredChildren(8) = {} + AXNode* node8 = tree.GetFromId(8); + unignored_iterator = node8->UnignoredChildrenBegin(); + EXPECT_EQ(node8->UnignoredChildrenEnd(), unignored_iterator); // empty list from ignored node with unignored children - // UnignoredChildren(10) = {} - AXNode* node10 = tree.GetFromId(10); - unignored_iterator = node10->UnignoredChildrenBegin(); - EXPECT_EQ(13, (*unignored_iterator).id()); + // UnignoredChildren(11) = {} + AXNode* node11 = tree.GetFromId(11); + unignored_iterator = node11->UnignoredChildrenBegin(); + EXPECT_EQ(14, (*unignored_iterator).id()); // Two UnignoredChildIterators from the same parent at the same position // should be equivalent, even in end position. @@ -1873,216 +1909,216 @@ TEST(AXTreeTest, UnignoredChildIterator) { TEST(AXTreeTest, UnignoredAccessors) { AXTreeUpdate tree_update; // (i) => node is ignored - // 0 + // 1 // |__________ // | | | - // 1(i) 2 3 + // 2(i) 3 4 // |_______________________ // | | | | - // 4 5 6(i) 7(i) + // 5 6 7(i) 8(i) // | | |________ // | | | | - // 8 9(i) 10(i) 11 + // 9 10(i) 11(i) 12 // | |____ // | | | - // 12(i) 13 14 + // 13(i) 14 15 // | | - // 15 16(i) - tree_update.root_id = 0; + // 16 17 + tree_update.root_id = 1; tree_update.nodes.resize(17); - tree_update.nodes[0].id = 0; - tree_update.nodes[0].child_ids = {1, 2, 3}; + tree_update.nodes[0].id = 1; + tree_update.nodes[0].child_ids = {2, 3, 4}; - tree_update.nodes[1].id = 1; - tree_update.nodes[1].child_ids = {4, 5, 6, 7}; + tree_update.nodes[1].id = 2; + tree_update.nodes[1].child_ids = {5, 6, 7, 8}; tree_update.nodes[1].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[2].id = 2; - tree_update.nodes[3].id = 3; + tree_update.nodes[2].id = 3; + tree_update.nodes[3].id = 4; - tree_update.nodes[4].id = 4; - tree_update.nodes[4].child_ids = {8}; + tree_update.nodes[4].id = 5; + tree_update.nodes[4].child_ids = {9}; - tree_update.nodes[5].id = 5; - tree_update.nodes[5].child_ids = {9}; + tree_update.nodes[5].id = 6; + tree_update.nodes[5].child_ids = {10}; - tree_update.nodes[6].id = 6; - tree_update.nodes[6].child_ids = {10, 11}; + tree_update.nodes[6].id = 7; + tree_update.nodes[6].child_ids = {11, 12}; tree_update.nodes[6].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[7].id = 7; + tree_update.nodes[7].id = 8; tree_update.nodes[7].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[8].id = 8; + tree_update.nodes[8].id = 9; - tree_update.nodes[9].id = 9; - tree_update.nodes[9].child_ids = {12}; + tree_update.nodes[9].id = 10; + tree_update.nodes[9].child_ids = {13}; tree_update.nodes[9].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[10].id = 10; - tree_update.nodes[10].child_ids = {13, 14}; + tree_update.nodes[10].id = 11; + tree_update.nodes[10].child_ids = {14, 15}; tree_update.nodes[10].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[11].id = 11; + tree_update.nodes[11].id = 12; - tree_update.nodes[12].id = 12; - tree_update.nodes[12].child_ids = {15}; + tree_update.nodes[12].id = 13; + tree_update.nodes[12].child_ids = {16}; tree_update.nodes[12].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[13].id = 13; - tree_update.nodes[13].child_ids = {16}; + tree_update.nodes[13].id = 14; + tree_update.nodes[13].child_ids = {17}; - tree_update.nodes[14].id = 14; + tree_update.nodes[14].id = 15; - tree_update.nodes[15].id = 15; + tree_update.nodes[15].id = 16; - tree_update.nodes[16].id = 16; + tree_update.nodes[16].id = 17; tree_update.nodes[16].AddState(ax::mojom::State::kIgnored); AXTree tree(tree_update); - EXPECT_EQ(3, tree.GetFromId(0)->GetLastUnignoredChild()->id()); - EXPECT_EQ(11, tree.GetFromId(1)->GetLastUnignoredChild()->id()); - EXPECT_EQ(nullptr, tree.GetFromId(2)->GetLastUnignoredChild()); + EXPECT_EQ(4, tree.GetFromId(1)->GetLastUnignoredChild()->id()); + EXPECT_EQ(12, tree.GetFromId(2)->GetLastUnignoredChild()->id()); EXPECT_EQ(nullptr, tree.GetFromId(3)->GetLastUnignoredChild()); - EXPECT_EQ(8, tree.GetFromId(4)->GetLastUnignoredChild()->id()); - EXPECT_EQ(15, tree.GetFromId(5)->GetLastUnignoredChild()->id()); - EXPECT_EQ(11, tree.GetFromId(6)->GetLastUnignoredChild()->id()); - EXPECT_EQ(nullptr, tree.GetFromId(7)->GetLastUnignoredChild()); + EXPECT_EQ(nullptr, tree.GetFromId(4)->GetLastUnignoredChild()); + EXPECT_EQ(9, tree.GetFromId(5)->GetLastUnignoredChild()->id()); + EXPECT_EQ(16, tree.GetFromId(6)->GetLastUnignoredChild()->id()); + EXPECT_EQ(12, tree.GetFromId(7)->GetLastUnignoredChild()->id()); EXPECT_EQ(nullptr, tree.GetFromId(8)->GetLastUnignoredChild()); - EXPECT_EQ(15, tree.GetFromId(9)->GetLastUnignoredChild()->id()); - EXPECT_EQ(14, tree.GetFromId(10)->GetLastUnignoredChild()->id()); - EXPECT_EQ(nullptr, tree.GetFromId(11)->GetLastUnignoredChild()); - EXPECT_EQ(15, tree.GetFromId(12)->GetLastUnignoredChild()->id()); - EXPECT_EQ(nullptr, tree.GetFromId(13)->GetLastUnignoredChild()); + EXPECT_EQ(nullptr, tree.GetFromId(9)->GetLastUnignoredChild()); + EXPECT_EQ(16, tree.GetFromId(10)->GetLastUnignoredChild()->id()); + EXPECT_EQ(15, tree.GetFromId(11)->GetLastUnignoredChild()->id()); + EXPECT_EQ(nullptr, tree.GetFromId(12)->GetLastUnignoredChild()); + EXPECT_EQ(16, tree.GetFromId(13)->GetLastUnignoredChild()->id()); EXPECT_EQ(nullptr, tree.GetFromId(14)->GetLastUnignoredChild()); EXPECT_EQ(nullptr, tree.GetFromId(15)->GetLastUnignoredChild()); EXPECT_EQ(nullptr, tree.GetFromId(16)->GetLastUnignoredChild()); + EXPECT_EQ(nullptr, tree.GetFromId(17)->GetLastUnignoredChild()); } TEST(AXTreeTest, UnignoredNextPreviousChild) { AXTreeUpdate tree_update; // (i) => node is ignored - // 0 + // 1 // |__________ // | | | - // 1(i) 2 3 + // 2(i) 3 4 // |_______________________ // | | | | - // 4 5 6(i) 7(i) + // 5 6 7(i) 8(i) // | | |________ // | | | | - // 8 9(i) 10(i) 11 + // 9 10(i) 11(i) 12 // | |____ // | | | - // 12(i) 13 14 + // 13(i) 14 15 // | - // 15 - tree_update.root_id = 0; + // 16 + tree_update.root_id = 1; tree_update.nodes.resize(16); - tree_update.nodes[0].id = 0; - tree_update.nodes[0].child_ids = {1, 2, 3}; + tree_update.nodes[0].id = 1; + tree_update.nodes[0].child_ids = {2, 3, 4}; - tree_update.nodes[1].id = 1; - tree_update.nodes[1].child_ids = {4, 5, 6, 7}; + tree_update.nodes[1].id = 2; + tree_update.nodes[1].child_ids = {5, 6, 7, 8}; tree_update.nodes[1].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[2].id = 2; - tree_update.nodes[3].id = 3; + tree_update.nodes[2].id = 3; + tree_update.nodes[3].id = 4; - tree_update.nodes[4].id = 4; - tree_update.nodes[4].child_ids = {8}; + tree_update.nodes[4].id = 5; + tree_update.nodes[4].child_ids = {9}; - tree_update.nodes[5].id = 5; - tree_update.nodes[5].child_ids = {9}; + tree_update.nodes[5].id = 6; + tree_update.nodes[5].child_ids = {10}; - tree_update.nodes[6].id = 6; - tree_update.nodes[6].child_ids = {10, 11}; + tree_update.nodes[6].id = 7; + tree_update.nodes[6].child_ids = {11, 12}; tree_update.nodes[6].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[7].id = 7; + tree_update.nodes[7].id = 8; tree_update.nodes[7].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[8].id = 8; + tree_update.nodes[8].id = 9; - tree_update.nodes[9].id = 9; - tree_update.nodes[9].child_ids = {12}; + tree_update.nodes[9].id = 10; + tree_update.nodes[9].child_ids = {13}; tree_update.nodes[9].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[10].id = 10; - tree_update.nodes[10].child_ids = {13, 14}; + tree_update.nodes[10].id = 11; + tree_update.nodes[10].child_ids = {14, 15}; tree_update.nodes[10].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[11].id = 11; + tree_update.nodes[11].id = 12; - tree_update.nodes[12].id = 12; - tree_update.nodes[12].child_ids = {15}; + tree_update.nodes[12].id = 13; + tree_update.nodes[12].child_ids = {16}; tree_update.nodes[12].AddState(ax::mojom::State::kIgnored); - tree_update.nodes[13].id = 13; + tree_update.nodes[13].id = 14; - tree_update.nodes[14].id = 14; + tree_update.nodes[14].id = 15; - tree_update.nodes[15].id = 15; + tree_update.nodes[15].id = 16; AXTree tree(tree_update); - EXPECT_EQ(nullptr, tree.GetFromId(0)->GetNextUnignoredSibling()); - EXPECT_EQ(nullptr, tree.GetFromId(0)->GetPreviousUnignoredSibling()); - - EXPECT_EQ(tree.GetFromId(2), tree.GetFromId(1)->GetNextUnignoredSibling()); + EXPECT_EQ(nullptr, tree.GetFromId(1)->GetNextUnignoredSibling()); EXPECT_EQ(nullptr, tree.GetFromId(1)->GetPreviousUnignoredSibling()); EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(2)->GetNextUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(11), - tree.GetFromId(2)->GetPreviousUnignoredSibling()); + EXPECT_EQ(nullptr, tree.GetFromId(2)->GetPreviousUnignoredSibling()); - EXPECT_EQ(nullptr, tree.GetFromId(3)->GetNextUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(2), + EXPECT_EQ(tree.GetFromId(4), tree.GetFromId(3)->GetNextUnignoredSibling()); + EXPECT_EQ(tree.GetFromId(12), tree.GetFromId(3)->GetPreviousUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(5), tree.GetFromId(4)->GetNextUnignoredSibling()); - EXPECT_EQ(nullptr, tree.GetFromId(4)->GetPreviousUnignoredSibling()); + EXPECT_EQ(nullptr, tree.GetFromId(4)->GetNextUnignoredSibling()); + EXPECT_EQ(tree.GetFromId(3), + tree.GetFromId(4)->GetPreviousUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(13), tree.GetFromId(5)->GetNextUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(4), - tree.GetFromId(5)->GetPreviousUnignoredSibling()); + EXPECT_EQ(tree.GetFromId(6), tree.GetFromId(5)->GetNextUnignoredSibling()); + EXPECT_EQ(nullptr, tree.GetFromId(5)->GetPreviousUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(2), tree.GetFromId(6)->GetNextUnignoredSibling()); + EXPECT_EQ(tree.GetFromId(14), tree.GetFromId(6)->GetNextUnignoredSibling()); EXPECT_EQ(tree.GetFromId(5), tree.GetFromId(6)->GetPreviousUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(2), tree.GetFromId(7)->GetNextUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(11), + EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(7)->GetNextUnignoredSibling()); + EXPECT_EQ(tree.GetFromId(6), tree.GetFromId(7)->GetPreviousUnignoredSibling()); - EXPECT_EQ(nullptr, tree.GetFromId(8)->GetNextUnignoredSibling()); - EXPECT_EQ(nullptr, tree.GetFromId(8)->GetPreviousUnignoredSibling()); + EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(8)->GetNextUnignoredSibling()); + EXPECT_EQ(tree.GetFromId(12), + tree.GetFromId(8)->GetPreviousUnignoredSibling()); EXPECT_EQ(nullptr, tree.GetFromId(9)->GetNextUnignoredSibling()); EXPECT_EQ(nullptr, tree.GetFromId(9)->GetPreviousUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(11), tree.GetFromId(10)->GetNextUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(5), - tree.GetFromId(10)->GetPreviousUnignoredSibling()); + EXPECT_EQ(nullptr, tree.GetFromId(10)->GetNextUnignoredSibling()); + EXPECT_EQ(nullptr, tree.GetFromId(10)->GetPreviousUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(2), tree.GetFromId(11)->GetNextUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(14), + EXPECT_EQ(tree.GetFromId(12), tree.GetFromId(11)->GetNextUnignoredSibling()); + EXPECT_EQ(tree.GetFromId(6), tree.GetFromId(11)->GetPreviousUnignoredSibling()); - EXPECT_EQ(nullptr, tree.GetFromId(12)->GetNextUnignoredSibling()); - EXPECT_EQ(nullptr, tree.GetFromId(12)->GetPreviousUnignoredSibling()); + EXPECT_EQ(tree.GetFromId(3), tree.GetFromId(12)->GetNextUnignoredSibling()); + EXPECT_EQ(tree.GetFromId(15), + tree.GetFromId(12)->GetPreviousUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(14), tree.GetFromId(13)->GetNextUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(5), - tree.GetFromId(13)->GetPreviousUnignoredSibling()); + EXPECT_EQ(nullptr, tree.GetFromId(13)->GetNextUnignoredSibling()); + EXPECT_EQ(nullptr, tree.GetFromId(13)->GetPreviousUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(11), tree.GetFromId(14)->GetNextUnignoredSibling()); - EXPECT_EQ(tree.GetFromId(13), + EXPECT_EQ(tree.GetFromId(15), tree.GetFromId(14)->GetNextUnignoredSibling()); + EXPECT_EQ(tree.GetFromId(6), tree.GetFromId(14)->GetPreviousUnignoredSibling()); - EXPECT_EQ(nullptr, tree.GetFromId(15)->GetNextUnignoredSibling()); - EXPECT_EQ(nullptr, tree.GetFromId(15)->GetPreviousUnignoredSibling()); + EXPECT_EQ(tree.GetFromId(12), tree.GetFromId(15)->GetNextUnignoredSibling()); + EXPECT_EQ(tree.GetFromId(14), + tree.GetFromId(15)->GetPreviousUnignoredSibling()); + + EXPECT_EQ(nullptr, tree.GetFromId(16)->GetNextUnignoredSibling()); + EXPECT_EQ(nullptr, tree.GetFromId(16)->GetPreviousUnignoredSibling()); } TEST(AXTreeTest, UnignoredSelection) { @@ -3196,20 +3232,78 @@ TEST(AXTreeTest, TestSetSizePosInSetSubtreeDeleted) { initial_state.nodes[2].role = ax::mojom::Role::kTreeItem; AXTree tree(initial_state); - // This should work normally. + AXNode* tree_node = tree.GetFromId(1); AXNode* item = tree.GetFromId(3); + + // This should work normally. EXPECT_OPTIONAL_EQ(2, item->GetPosInSet()); EXPECT_OPTIONAL_EQ(2, item->GetSetSize()); - // Use test observer to assert posinset and setsize are 0. - TestAXTreeObserver test_observer(&tree); - test_observer.call_posinset_and_setsize = true; // Remove item from tree. AXTreeUpdate tree_update = initial_state; tree_update.nodes.resize(1); tree_update.nodes[0].child_ids = {2}; ASSERT_TRUE(tree.Unserialize(tree_update)); + + // These values are lazily created, so to test that they fail when + // called in the middle of a tree update, fake the update state. + tree.SetTreeUpdateInProgressState(true); + ASSERT_FALSE(tree_node->GetPosInSet()); + ASSERT_FALSE(tree_node->GetSetSize()); + + // Then reset the state to make sure we have the expected values + // after |Unserialize|. + tree.SetTreeUpdateInProgressState(false); + ASSERT_FALSE(tree_node->GetPosInSet()); + EXPECT_OPTIONAL_EQ(1, tree_node->GetSetSize()); +} + +// Tests that GetPosInSet and GetSetSize work when there are ignored nodes. +TEST(AXTreeTest, TestSetSizePosInSetIgnoredItem) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(3); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].role = ax::mojom::Role::kTree; + initial_state.nodes[0].child_ids = {2, 3}; + initial_state.nodes[1].id = 2; + initial_state.nodes[1].role = ax::mojom::Role::kTreeItem; + initial_state.nodes[2].id = 3; + initial_state.nodes[2].role = ax::mojom::Role::kTreeItem; + AXTree tree(initial_state); + + AXNode* tree_node = tree.GetFromId(1); + AXNode* item1 = tree.GetFromId(2); + AXNode* item2 = tree.GetFromId(3); + + // This should work normally. + ASSERT_FALSE(tree_node->GetPosInSet()); + EXPECT_OPTIONAL_EQ(2, tree_node->GetSetSize()); + + EXPECT_OPTIONAL_EQ(1, item1->GetPosInSet()); + EXPECT_OPTIONAL_EQ(2, item1->GetSetSize()); + + EXPECT_OPTIONAL_EQ(2, item2->GetPosInSet()); + EXPECT_OPTIONAL_EQ(2, item2->GetSetSize()); + + // Remove item from tree. + AXTreeUpdate tree_update; + tree_update.nodes.resize(1); + tree_update.nodes[0] = initial_state.nodes[1]; + tree_update.nodes[0].AddState(ax::mojom::State::kIgnored); + + ASSERT_TRUE(tree.Unserialize(tree_update)); + + ASSERT_FALSE(tree_node->GetPosInSet()); + EXPECT_OPTIONAL_EQ(1, tree_node->GetSetSize()); + + // Ignored nodes are not part of ordered sets. + EXPECT_FALSE(item1->GetPosInSet()); + EXPECT_FALSE(item1->GetSetSize()); + + EXPECT_OPTIONAL_EQ(1, item2->GetPosInSet()); + EXPECT_OPTIONAL_EQ(1, item2->GetSetSize()); } // Tests that kPopUpButtons are assigned the SetSize of the wrapped @@ -3243,4 +3337,267 @@ TEST(AXTreeTest, TestSetSizePosInSetPopUpButton) { EXPECT_OPTIONAL_EQ(2, popup_button_2->GetSetSize()); } +TEST(AXTreeTest, OnNodeWillBeDeletedHasValidUnignoredParent) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(3); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea; + initial_state.nodes[0].child_ids = {2}; + initial_state.nodes[1].id = 2; + initial_state.nodes[1].role = ax::mojom::Role::kGenericContainer; + initial_state.nodes[1].child_ids = {3}; + initial_state.nodes[2].id = 3; + initial_state.nodes[2].role = ax::mojom::Role::kGenericContainer; + AXTree tree(initial_state); + + AXTreeUpdate tree_update; + tree_update.nodes.resize(1); + // Remove child from node:2, and add State::kIgnored + tree_update.nodes[0] = initial_state.nodes[1]; + tree_update.nodes[0].AddState(ax::mojom::State::kIgnored); + tree_update.nodes[0].child_ids.clear(); + + // Before node:3 is deleted, the unignored parent is node:2. + // Assert that this is the case in |OnNodeWillBeDeleted|. + TestAXTreeObserver test_observer(&tree); + test_observer.unignored_parent_id_before_node_deleted = 2; + ASSERT_TRUE(tree.Unserialize(tree_update)); +} + +// Tests a fringe scenario that may happen if multiple AXTreeUpdates are merged. +// Make sure that we correctly Unserialize if a newly created node is deleted, +// and possibly recreated later. +TEST(AXTreeTest, SingleUpdateDeletesNewlyCreatedChildNode) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea; + AXTree tree(initial_state); + + AXTreeUpdate tree_update; + tree_update.nodes.resize(6); + // Add child node:2 + tree_update.nodes[0] = initial_state.nodes[0]; + tree_update.nodes[0].child_ids = {2}; + tree_update.nodes[1].id = 2; + tree_update.nodes[1].role = ax::mojom::Role::kGenericContainer; + // Remove child node:2 + tree_update.nodes[2] = initial_state.nodes[0]; + // Add child node:2 + tree_update.nodes[3] = initial_state.nodes[0]; + tree_update.nodes[3].child_ids = {2}; + tree_update.nodes[4].id = 2; + tree_update.nodes[4].role = ax::mojom::Role::kGenericContainer; + // Remove child node:2 + tree_update.nodes[5] = initial_state.nodes[0]; + + ASSERT_TRUE(tree.Unserialize(tree_update)) << tree.error(); + + ASSERT_EQ( + "AXTree\n" + "id=1 rootWebArea (0, 0)-(0, 0) actions=\n", + tree.ToString()); + + // Unserialize again, but with another add child. + tree_update.nodes.resize(8); + tree_update.nodes[6] = initial_state.nodes[0]; + tree_update.nodes[6].child_ids = {2}; + tree_update.nodes[7].id = 2; + tree_update.nodes[7].role = ax::mojom::Role::kGenericContainer; + ASSERT_TRUE(tree.Unserialize(tree_update)) << tree.error(); + + ASSERT_EQ( + "AXTree\n" + "id=1 rootWebArea (0, 0)-(0, 0) actions= child_ids=2\n" + " id=2 genericContainer (0, 0)-(0, 0) actions=\n", + tree.ToString()); +} + +// Tests a fringe scenario that may happen if multiple AXTreeUpdates are merged. +// Make sure that we correctly Unserialize if a node is reparented multiple +// times. +TEST(AXTreeTest, SingleUpdateReparentsNodeMultipleTimes) { + // ++{kRootWebArea, 1} + // ++++{kList, 2} + // ++++++{kListItem, 4} + // ++++{kList, 3} + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(4); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea; + initial_state.nodes[0].child_ids = {2, 3}; + initial_state.nodes[1].id = 2; + initial_state.nodes[1].role = ax::mojom::Role::kList; + initial_state.nodes[1].child_ids = {4}; + initial_state.nodes[2].id = 3; + initial_state.nodes[2].role = ax::mojom::Role::kList; + initial_state.nodes[3].id = 4; + initial_state.nodes[3].role = ax::mojom::Role::kListItem; + AXTree tree(initial_state); + + AXTreeUpdate tree_update; + tree_update.nodes.resize(6); + // Remove child node:4 + tree_update.nodes[0].id = 2; + tree_update.nodes[0].role = ax::mojom::Role::kList; + // Reparent child node:4 onto node:3 + tree_update.nodes[1].id = 3; + tree_update.nodes[1].role = ax::mojom::Role::kList; + tree_update.nodes[1].child_ids = {4}; + tree_update.nodes[2].id = 4; + tree_update.nodes[2].role = ax::mojom::Role::kListItem; + // Remove child ndoe:4 + tree_update.nodes[3].id = 3; + tree_update.nodes[3].role = ax::mojom::Role::kList; + // Reparent child node:4 onto node:2 + tree_update.nodes[4].id = 2; + tree_update.nodes[4].role = ax::mojom::Role::kList; + tree_update.nodes[4].child_ids = {4}; + tree_update.nodes[5].id = 4; + tree_update.nodes[5].role = ax::mojom::Role::kListItem; + + ASSERT_TRUE(tree.Unserialize(tree_update)) << tree.error(); + EXPECT_EQ( + "AXTree\nid=1 rootWebArea (0, 0)-(0, 0) actions= child_ids=2,3\n" + " id=2 list (0, 0)-(0, 0) actions= child_ids=4\n" + " id=4 listItem (0, 0)-(0, 0) actions=\n" + " id=3 list (0, 0)-(0, 0) actions=\n", + tree.ToString()); + + // Unserialize again, but with another reparent. + tree_update.nodes.resize(9); + tree_update.nodes[6] = tree_update.nodes[0]; + tree_update.nodes[7] = tree_update.nodes[1]; + tree_update.nodes[8] = tree_update.nodes[2]; + + ASSERT_TRUE(tree.Unserialize(tree_update)) << tree.error(); + EXPECT_EQ( + "AXTree\nid=1 rootWebArea (0, 0)-(0, 0) actions= child_ids=2,3\n" + " id=2 list (0, 0)-(0, 0) actions=\n" + " id=3 list (0, 0)-(0, 0) actions= child_ids=4\n" + " id=4 listItem (0, 0)-(0, 0) actions=\n", + tree.ToString()); +} + +// Tests a fringe scenario that may happen if multiple AXTreeUpdates are merged. +// Make sure that we correctly Unserialize if a newly created node toggles its +// ignored state. +TEST(AXTreeTest, SingleUpdateIgnoresNewlyCreatedUnignoredChildNode) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea; + AXTree tree(initial_state); + + AXTreeUpdate tree_update; + tree_update.nodes.resize(3); + // Add child node:2 + tree_update.nodes[0] = initial_state.nodes[0]; + tree_update.nodes[0].child_ids = {2}; + tree_update.nodes[1].id = 2; + tree_update.nodes[1].role = ax::mojom::Role::kGenericContainer; + // Add State::kIgnored to node:2 + tree_update.nodes[2] = tree_update.nodes[1]; + tree_update.nodes[2].AddState(ax::mojom::State::kIgnored); + + ASSERT_TRUE(tree.Unserialize(tree_update)) << tree.error(); + + ASSERT_EQ( + "AXTree\n" + "id=1 rootWebArea (0, 0)-(0, 0) actions= child_ids=2\n" + " id=2 genericContainer IGNORED (0, 0)-(0, 0) actions=\n", + tree.ToString()); +} + +// Tests a fringe scenario that may happen if multiple AXTreeUpdates are merged. +// Make sure that we correctly Unserialize if a newly created node toggles its +// ignored state. +TEST(AXTreeTest, SingleUpdateTogglesIgnoredStateAfterCreatingNode) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea; + AXTree tree(initial_state); + + ASSERT_EQ( + "AXTree\n" + "id=1 rootWebArea (0, 0)-(0, 0) actions=\n", + tree.ToString()); + + AXTreeUpdate tree_update; + tree_update.nodes.resize(5); + // Add child node:2, node:3 + tree_update.nodes[0] = initial_state.nodes[0]; + tree_update.nodes[0].child_ids = {2, 3}; + tree_update.nodes[1].id = 2; + tree_update.nodes[1].role = ax::mojom::Role::kGenericContainer; + tree_update.nodes[2].id = 3; + tree_update.nodes[2].role = ax::mojom::Role::kGenericContainer; + tree_update.nodes[2].AddState(ax::mojom::State::kIgnored); + // Add State::kIgnored to node:2 + tree_update.nodes[3] = tree_update.nodes[1]; + tree_update.nodes[3].AddState(ax::mojom::State::kIgnored); + // Remove State::kIgnored from node:3 + tree_update.nodes[4] = tree_update.nodes[2]; + tree_update.nodes[4].RemoveState(ax::mojom::State::kIgnored); + + ASSERT_TRUE(tree.Unserialize(tree_update)) << tree.error(); + + ASSERT_EQ( + "AXTree\n" + "id=1 rootWebArea (0, 0)-(0, 0) actions= child_ids=2,3\n" + " id=2 genericContainer IGNORED (0, 0)-(0, 0) actions=\n" + " id=3 genericContainer (0, 0)-(0, 0) actions=\n", + tree.ToString()); +} + +// Tests a fringe scenario that may happen if multiple AXTreeUpdates are merged. +// Make sure that we correctly Unserialize if a node toggles its ignored state +// and is then removed from the tree. +TEST(AXTreeTest, SingleUpdateTogglesIgnoredStateBeforeDestroyingNode) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(3); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea; + initial_state.nodes[0].child_ids = {2, 3}; + initial_state.nodes[1].id = 2; + initial_state.nodes[1].role = ax::mojom::Role::kGenericContainer; + initial_state.nodes[2].id = 3; + initial_state.nodes[2].role = ax::mojom::Role::kGenericContainer; + initial_state.nodes[2].AddState(ax::mojom::State::kIgnored); + AXTree tree(initial_state); + + ASSERT_EQ( + "AXTree\n" + "id=1 rootWebArea (0, 0)-(0, 0) actions= child_ids=2,3\n" + " id=2 genericContainer (0, 0)-(0, 0) actions=\n" + " id=3 genericContainer IGNORED (0, 0)-(0, 0) actions=\n", + tree.ToString()); + + AXTreeUpdate tree_update; + tree_update.nodes.resize(3); + // Add State::kIgnored to node:2 + tree_update.nodes[0] = initial_state.nodes[1]; + tree_update.nodes[0].AddState(ax::mojom::State::kIgnored); + // Remove State::kIgnored from node:3 + tree_update.nodes[1] = initial_state.nodes[2]; + tree_update.nodes[1].RemoveState(ax::mojom::State::kIgnored); + // Remove child node:2, node:3 + tree_update.nodes[2] = initial_state.nodes[0]; + tree_update.nodes[2].child_ids.clear(); + + ASSERT_TRUE(tree.Unserialize(tree_update)) << tree.error(); + + ASSERT_EQ( + "AXTree\n" + "id=1 rootWebArea (0, 0)-(0, 0) actions=\n", + tree.ToString()); +} + } // namespace ui diff --git a/chromium/ui/accessibility/platform/ax_fragment_root_win_unittest.cc b/chromium/ui/accessibility/platform/ax_fragment_root_win_unittest.cc index 1d35919796c..635e6f855e2 100644 --- a/chromium/ui/accessibility/platform/ax_fragment_root_win_unittest.cc +++ b/chromium/ui/accessibility/platform/ax_fragment_root_win_unittest.cc @@ -52,18 +52,18 @@ TEST_F(AXFragmentRootTest, TestUIAGetRuntimeId) { TEST_F(AXFragmentRootTest, TestUIAElementProviderFromPoint) { AXNodeData root_data; - root_data.id = 0; - root_data.child_ids.push_back(1); - root_data.child_ids.push_back(2); + root_data.id = 1; root_data.relative_bounds.bounds = gfx::RectF(0, 0, 80, 80); AXNodeData element1_data; - element1_data.id = 1; + element1_data.id = 2; element1_data.relative_bounds.bounds = gfx::RectF(0, 0, 50, 50); + root_data.child_ids.push_back(element1_data.id); AXNodeData element2_data; - element2_data.id = 2; + element2_data.id = 3; element2_data.relative_bounds.bounds = gfx::RectF(0, 50, 30, 30); + root_data.child_ids.push_back(element2_data.id); Init(root_data, element1_data, element2_data); InitFragmentRoot(); @@ -96,15 +96,15 @@ TEST_F(AXFragmentRootTest, TestUIAElementProviderFromPoint) { TEST_F(AXFragmentRootTest, TestUIAGetFocus) { AXNodeData root_data; - root_data.id = 0; - root_data.child_ids.push_back(1); - root_data.child_ids.push_back(2); + root_data.id = 1; AXNodeData element1_data; - element1_data.id = 1; + element1_data.id = 2; + root_data.child_ids.push_back(element1_data.id); AXNodeData element2_data; - element2_data.id = 2; + element2_data.id = 3; + root_data.child_ids.push_back(element2_data.id); Init(root_data, element1_data, element2_data); InitFragmentRoot(); diff --git a/chromium/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc b/chromium/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc index ae0050a7dac..e9971bd618a 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc @@ -632,20 +632,20 @@ TEST_F(AXPlatformNodeAuraLinuxTest, DISABLED_TestAtkObjectIntAttributes) { TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkComponentRefAtPoint) { AXNodeData root; - root.id = 0; - root.child_ids.push_back(1); - root.child_ids.push_back(2); + root.id = 1; root.relative_bounds.bounds = gfx::RectF(0, 0, 30, 30); AXNodeData node1; - node1.id = 1; + node1.id = 2; node1.relative_bounds.bounds = gfx::RectF(0, 0, 10, 10); node1.SetName("Name1"); + root.child_ids.push_back(node1.id); AXNodeData node2; - node2.id = 2; + node2.id = 3; node2.relative_bounds.bounds = gfx::RectF(20, 20, 10, 10); node2.SetName("Name2"); + root.child_ids.push_back(node2.id); Init(root, node1, node2); diff --git a/chromium/ui/accessibility/platform/ax_platform_node_textchildprovider_win_unittest.cc b/chromium/ui/accessibility/platform/ax_platform_node_textchildprovider_win_unittest.cc index 3560db35030..b8825da748f 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_textchildprovider_win_unittest.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_textchildprovider_win_unittest.cc @@ -26,33 +26,33 @@ class AXPlatformNodeTextChildProviderTest : public ui::AXPlatformNodeWinTest { // nontext text text void SetUp() override { ui::AXNodeData root; - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kRootWebArea; ui::AXNodeData nontext_child_of_root; - nontext_child_of_root.id = 1; + nontext_child_of_root.id = 2; nontext_child_of_root.role = ax::mojom::Role::kGroup; - root.child_ids.push_back(1); + root.child_ids.push_back(nontext_child_of_root.id); ui::AXNodeData text_child_of_root; - text_child_of_root.id = 2; + text_child_of_root.id = 3; text_child_of_root.role = ax::mojom::Role::kStaticText; - root.child_ids.push_back(2); + root.child_ids.push_back(text_child_of_root.id); ui::AXNodeData nontext_child_of_nontext; - nontext_child_of_nontext.id = 3; + nontext_child_of_nontext.id = 4; nontext_child_of_nontext.role = ax::mojom::Role::kGroup; - nontext_child_of_root.child_ids.push_back(3); + nontext_child_of_root.child_ids.push_back(nontext_child_of_nontext.id); ui::AXNodeData text_child_of_nontext; - text_child_of_nontext.id = 4; + text_child_of_nontext.id = 5; text_child_of_nontext.role = ax::mojom::Role::kStaticText; - nontext_child_of_root.child_ids.push_back(4); + nontext_child_of_root.child_ids.push_back(text_child_of_nontext.id); ui::AXNodeData text_child_of_text; - text_child_of_text.id = 5; + text_child_of_text.id = 6; text_child_of_text.role = ax::mojom::Role::kStaticText; - text_child_of_root.child_ids.push_back(5); + text_child_of_root.child_ids.push_back(text_child_of_text.id); ui::AXTreeUpdate update; ui::AXTreeData tree_data; diff --git a/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc index c854497c1f1..706563f81f2 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc @@ -2292,15 +2292,15 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderMoveEndpointByUnitTextField) { ui::AXNodeData root_data; - root_data.id = 0; + root_data.id = 1; root_data.role = ax::mojom::Role::kRootWebArea; ui::AXNodeData group1_data; - group1_data.id = 1; + group1_data.id = 2; group1_data.role = ax::mojom::Role::kGenericContainer; ui::AXNodeData text_data; - text_data.id = 2; + text_data.id = 3; text_data.role = ax::mojom::Role::kStaticText; std::string text_content = "some text"; text_data.SetName(text_content); @@ -2313,15 +2313,15 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, word_end_offsets); ui::AXNodeData text_input_data; - text_input_data.id = 3; + text_input_data.id = 4; text_input_data.role = ax::mojom::Role::kTextField; ui::AXNodeData group2_data; - group2_data.id = 4; + group2_data.id = 5; group2_data.role = ax::mojom::Role::kGenericContainer; ui::AXNodeData more_text_data; - more_text_data.id = 5; + more_text_data.id = 6; more_text_data.role = ax::mojom::Role::kStaticText; text_content = "more text"; more_text_data.SetName(text_content); @@ -2333,7 +2333,7 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, word_end_offsets); ui::AXNodeData empty_text_data; - empty_text_data.id = 6; + empty_text_data.id = 7; empty_text_data.role = ax::mojom::Role::kStaticText; text_content = ""; empty_text_data.SetName(text_content); @@ -2344,10 +2344,10 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, empty_text_data.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds, word_end_offsets); - root_data.child_ids = {1, 3, 4}; - group1_data.child_ids = {2}; - text_input_data.child_ids = {6}; - group2_data.child_ids = {5}; + root_data.child_ids = {group1_data.id, text_input_data.id, group2_data.id}; + group1_data.child_ids = {text_data.id}; + text_input_data.child_ids = {empty_text_data.id}; + group2_data.child_ids = {more_text_data.id}; ui::AXTreeUpdate update; ui::AXTreeData tree_data; @@ -2839,28 +2839,28 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderGetChildren) { // | | // text_node3 text_node4 ui::AXNodeData root_data; - root_data.id = 0; + root_data.id = 1; root_data.role = ax::mojom::Role::kRootWebArea; ui::AXNodeData text_node1; - text_node1.id = 1; + text_node1.id = 2; text_node1.role = ax::mojom::Role::kStaticText; - root_data.child_ids.push_back(1); + root_data.child_ids.push_back(text_node1.id); ui::AXNodeData text_node2; - text_node2.id = 2; + text_node2.id = 3; text_node2.role = ax::mojom::Role::kStaticText; - root_data.child_ids.push_back(2); + root_data.child_ids.push_back(text_node2.id); ui::AXNodeData text_node3; - text_node3.id = 3; + text_node3.id = 4; text_node3.role = ax::mojom::Role::kStaticText; - text_node1.child_ids.push_back(3); + text_node1.child_ids.push_back(text_node3.id); ui::AXNodeData text_node4; - text_node4.id = 4; + text_node4.id = 5; text_node4.role = ax::mojom::Role::kStaticText; - text_node1.child_ids.push_back(4); + text_node1.child_ids.push_back(text_node4.id); ui::AXTreeUpdate update; ui::AXTreeData tree_data; @@ -3261,19 +3261,20 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderGetAttributeValueNotSupported) { ui::AXNodeData root_data; - root_data.id = 0; + root_data.id = 1; root_data.role = ax::mojom::Role::kRootWebArea; - root_data.child_ids = {1, 2}; ui::AXNodeData text_data_first; - text_data_first.id = 1; + text_data_first.id = 2; text_data_first.role = ax::mojom::Role::kStaticText; text_data_first.SetName("first"); + root_data.child_ids.push_back(text_data_first.id); ui::AXNodeData text_data_second; - text_data_second.id = 2; + text_data_second.id = 3; text_data_second.role = ax::mojom::Role::kStaticText; text_data_second.SetName("second"); + root_data.child_ids.push_back(text_data_second.id); ui::AXTreeUpdate update; ui::AXTreeData tree_data; @@ -4028,7 +4029,7 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, TEST_F(AXPlatformNodeTextRangeProviderTest, ElementNotAvailable) { AXNodeData root_ax_node_data; - root_ax_node_data.id = 0; + root_ax_node_data.id = 1; root_ax_node_data.role = ax::mojom::Role::kRootWebArea; Init(root_ax_node_data); diff --git a/chromium/ui/accessibility/platform/ax_platform_node_unittest.cc b/chromium/ui/accessibility/platform/ax_platform_node_unittest.cc index 9409c8acf33..6f6562729af 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_unittest.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_unittest.cc @@ -200,14 +200,14 @@ AXTreeUpdate AXPlatformNodeTest::AXPlatformNodeTest::Build3X3Table() { */ AXNodeData table; - table.id = 0; + table.id = 1; table.role = ax::mojom::Role::kTable; table.AddIntAttribute(ax::mojom::IntAttribute::kTableRowCount, 3); table.AddIntAttribute(ax::mojom::IntAttribute::kTableColumnCount, 3); table.child_ids.push_back(50); // Header - table.child_ids.push_back(1); // Row 1 + table.child_ids.push_back(2); // Row 1 table.child_ids.push_back(10); // Row 2 // Table column header @@ -249,44 +249,41 @@ AXTreeUpdate AXPlatformNodeTest::AXPlatformNodeTest::Build3X3Table() { // Row 1 AXNodeData table_row_1; - table_row_1.id = 1; + table_row_1.id = 2; table_row_1.role = ax::mojom::Role::kRow; - table_row_1.child_ids.push_back(2); - table_row_1.child_ids.push_back(3); - table_row_1.child_ids.push_back(4); AXNodeData table_row_header_1; - table_row_header_1.id = 2; + table_row_header_1.id = 3; table_row_header_1.role = ax::mojom::Role::kRowHeader; table_row_header_1.SetName("row header 1"); table_row_header_1.AddIntAttribute( ax::mojom::IntAttribute::kTableCellRowIndex, 1); table_row_header_1.AddIntAttribute( ax::mojom::IntAttribute::kTableCellColumnIndex, 0); + table_row_1.child_ids.push_back(table_row_header_1.id); AXNodeData table_cell_1; - table_cell_1.id = 3; + table_cell_1.id = 4; table_cell_1.role = ax::mojom::Role::kCell; table_cell_1.SetName("1"); table_cell_1.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, 1); table_cell_1.AddIntAttribute(ax::mojom::IntAttribute::kTableCellColumnIndex, 1); + table_row_1.child_ids.push_back(table_cell_1.id); AXNodeData table_cell_2; - table_cell_2.id = 4; + table_cell_2.id = 5; table_cell_2.role = ax::mojom::Role::kCell; table_cell_2.SetName("2"); table_cell_2.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, 1); table_cell_2.AddIntAttribute(ax::mojom::IntAttribute::kTableCellColumnIndex, 2); + table_row_1.child_ids.push_back(table_cell_2.id); // Row 2 AXNodeData table_row_2; table_row_2.id = 10; table_row_2.role = ax::mojom::Role::kRow; - table_row_2.child_ids.push_back(11); - table_row_2.child_ids.push_back(12); - table_row_2.child_ids.push_back(13); AXNodeData table_row_header_2; table_row_header_2.id = 11; @@ -299,6 +296,7 @@ AXTreeUpdate AXPlatformNodeTest::AXPlatformNodeTest::Build3X3Table() { ax::mojom::IntAttribute::kTableCellRowIndex, 2); table_row_header_2.AddIntAttribute( ax::mojom::IntAttribute::kTableCellColumnIndex, 0); + table_row_2.child_ids.push_back(table_row_header_2.id); AXNodeData table_cell_3; table_cell_3.id = 12; @@ -307,6 +305,7 @@ AXTreeUpdate AXPlatformNodeTest::AXPlatformNodeTest::Build3X3Table() { table_cell_3.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, 2); table_cell_3.AddIntAttribute(ax::mojom::IntAttribute::kTableCellColumnIndex, 1); + table_row_2.child_ids.push_back(table_cell_3.id); AXNodeData table_cell_4; table_cell_4.id = 13; @@ -315,6 +314,7 @@ AXTreeUpdate AXPlatformNodeTest::AXPlatformNodeTest::Build3X3Table() { table_cell_4.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, 2); table_cell_4.AddIntAttribute(ax::mojom::IntAttribute::kTableCellColumnIndex, 2); + table_row_2.child_ids.push_back(table_cell_4.id); AXTreeUpdate update; update.root_id = table.id; @@ -411,14 +411,14 @@ AXTreeUpdate AXPlatformNodeTest::BuildListBox( bool option_3_is_selected, ax::mojom::State additional_state /* ax::mojom::State::kNone */) { AXNodeData listbox; - listbox.id = 0; + listbox.id = 1; listbox.SetName("ListBox"); listbox.role = ax::mojom::Role::kListBox; if (additional_state != ax::mojom::State::kNone) listbox.AddState(additional_state); AXNodeData option_1; - option_1.id = 1; + option_1.id = 2; option_1.SetName("Option1"); option_1.role = ax::mojom::Role::kListBoxOption; if (option_1_is_selected) @@ -426,7 +426,7 @@ AXTreeUpdate AXPlatformNodeTest::BuildListBox( listbox.child_ids.push_back(option_1.id); AXNodeData option_2; - option_2.id = 2; + option_2.id = 3; option_2.SetName("Option2"); option_2.role = ax::mojom::Role::kListBoxOption; if (option_2_is_selected) @@ -434,7 +434,7 @@ AXTreeUpdate AXPlatformNodeTest::BuildListBox( listbox.child_ids.push_back(option_2.id); AXNodeData option_3; - option_3.id = 3; + option_3.id = 4; option_3.SetName("Option3"); option_3.role = ax::mojom::Role::kListBoxOption; if (option_3_is_selected) diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win.cc b/chromium/ui/accessibility/platform/ax_platform_node_win.cc index 46c7c431201..a47f1070c5c 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_win.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_win.cc @@ -1115,9 +1115,8 @@ IFACEMETHODIMP AXPlatformNodeWin::get_accState(VARIANT var_id, VARIANT* state) { IFACEMETHODIMP AXPlatformNodeWin::get_accHelp(VARIANT var_id, BSTR* help) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_HELP); - AXPlatformNodeWin* target; - COM_OBJECT_VALIDATE_VAR_ID_1_ARG_AND_GET_TARGET(var_id, help, target); - return target->GetHelpText(help); + COM_OBJECT_VALIDATE_1_ARG(help); + return S_FALSE; } IFACEMETHODIMP AXPlatformNodeWin::get_accValue(VARIANT var_id, BSTR* value) { @@ -3827,8 +3826,20 @@ IFACEMETHODIMP AXPlatformNodeWin::GetPropertyValue(PROPERTYID property_id, break; case UIA_HelpTextPropertyId: - V_VT(result) = VT_BSTR; - GetHelpText(&V_BSTR(result)); + if (HasStringAttribute(ax::mojom::StringAttribute::kPlaceholder)) { + V_VT(result) = VT_BSTR; + GetStringAttributeAsBstr(ax::mojom::StringAttribute::kPlaceholder, + &V_BSTR(result)); + } else if (data.GetNameFrom() == ax::mojom::NameFrom::kPlaceholder || + data.GetNameFrom() == ax::mojom::NameFrom::kTitle) { + V_VT(result) = VT_BSTR; + GetStringAttributeAsBstr(ax::mojom::StringAttribute::kName, + &V_BSTR(result)); + } else if (HasStringAttribute(ax::mojom::StringAttribute::kTooltip)) { + V_VT(result) = VT_BSTR; + GetStringAttributeAsBstr(ax::mojom::StringAttribute::kTooltip, + &V_BSTR(result)); + } break; case UIA_IsContentElementPropertyId: @@ -6911,21 +6922,6 @@ HRESULT AXPlatformNodeWin::AllocateComArrayFromVector( return S_OK; } -HRESULT AXPlatformNodeWin::GetHelpText(BSTR* helpText) { - if (HasStringAttribute(ax::mojom::StringAttribute::kPlaceholder)) { - return GetStringAttributeAsBstr(ax::mojom::StringAttribute::kPlaceholder, - helpText); - } else if (GetData().GetNameFrom() == ax::mojom::NameFrom::kPlaceholder || - GetData().GetNameFrom() == ax::mojom::NameFrom::kTitle) { - return GetStringAttributeAsBstr(ax::mojom::StringAttribute::kName, - helpText); - } else if (HasStringAttribute(ax::mojom::StringAttribute::kTooltip)) { - return GetStringAttributeAsBstr(ax::mojom::StringAttribute::kTooltip, - helpText); - } - return S_FALSE; -} - // TODO(dmazzoni): Remove this function once combo box refactoring is // complete. bool AXPlatformNodeWin::IsAncestorComboBox() { diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win.h b/chromium/ui/accessibility/platform/ax_platform_node_win.h index 2dfbbbc414f..518143a76e9 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_win.h +++ b/chromium/ui/accessibility/platform/ax_platform_node_win.h @@ -1234,9 +1234,6 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) // Helper method for mutating the ISelectionItemProvider selected state HRESULT ISelectionItemProviderSetSelected(bool selected); - // Helper method for getting the help text property. - HRESULT GetHelpText(BSTR* helpText); - // // Getters for UIA GetTextAttributeValue // diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc index 5c4d87aa356..ee082da074b 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc @@ -439,20 +439,20 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleDetachedObject) { TEST_F(AXPlatformNodeWinTest, TestIAccessibleHitTest) { AXNodeData root; - root.id = 0; - root.child_ids.push_back(1); - root.child_ids.push_back(2); + root.id = 1; root.relative_bounds.bounds = gfx::RectF(0, 0, 30, 30); AXNodeData node1; - node1.id = 1; + node1.id = 2; node1.relative_bounds.bounds = gfx::RectF(0, 0, 10, 10); node1.SetName("Name1"); + root.child_ids.push_back(node1.id); AXNodeData node2; - node2.id = 2; + node2.id = 3; node2.relative_bounds.bounds = gfx::RectF(20, 20, 10, 10); node2.SetName("Name2"); + root.child_ids.push_back(node2.id); Init(root, node1, node2); @@ -542,114 +542,19 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleShortcut) { root_obj->get_accKeyboardShortcut(bad_id, k2.Receive())); } -TEST_F(AXPlatformNodeWinTest, TestIAccessibleHelpText) { - AXNodeData root; - root.id = 0; - - // Test Placeholder StringAttribute is exposed. - AXNodeData node1; - node1.id = 1; - node1.SetName("name-from-title"); - node1.AddIntAttribute(ax::mojom::IntAttribute::kNameFrom, - static_cast<int>(ax::mojom::NameFrom::kTitle)); - node1.AddStringAttribute(ax::mojom::StringAttribute::kPlaceholder, - "placeholder"); - root.child_ids.push_back(node1.id); - - // Test NameFrom Title is exposed. - AXNodeData node2; - node2.id = 2; - node2.SetName("name-from-title"); - node2.AddIntAttribute(ax::mojom::IntAttribute::kNameFrom, - static_cast<int>(ax::mojom::NameFrom::kTitle)); - root.child_ids.push_back(node2.id); - - // Test NameFrom Placeholder is exposed. - AXNodeData node3; - node3.id = 3; - node3.SetName("name-from-placeholder"); - node3.AddIntAttribute(ax::mojom::IntAttribute::kNameFrom, - static_cast<int>(ax::mojom::NameFrom::kPlaceholder)); - root.child_ids.push_back(node3.id); - - // Test Tooltip StringAttribute is exposed. - AXNodeData node4; - node4.id = 4; - node4.SetName("name-from-attribute"); - node4.AddIntAttribute(ax::mojom::IntAttribute::kNameFrom, - static_cast<int>(ax::mojom::NameFrom::kAttribute)); - node4.AddStringAttribute(ax::mojom::StringAttribute::kTooltip, "tooltip"); - root.child_ids.push_back(node4.id); - - // Test StringAttribute is not exposed without explicit - // Placeholder / Title / Tooltip. - AXNodeData node5; - node5.id = 5; - node5.SetName("name-from-attribute"); - node5.AddIntAttribute(ax::mojom::IntAttribute::kNameFrom, - static_cast<int>(ax::mojom::NameFrom::kAttribute)); - root.child_ids.push_back(node5.id); - - Init(root, node1, node2, node3, node4, node5); - - auto* root_node = GetRootNode(); - - ScopedBstr helpText1; - ComPtr<IAccessible> child_node1( - IAccessibleFromNode(root_node->children()[0])); - EXPECT_EQ(S_OK, child_node1->get_accHelp(SELF, helpText1.Receive())); - EXPECT_STREQ(L"placeholder", helpText1); - - ScopedBstr helpText2; - ComPtr<IAccessible> child_node2( - IAccessibleFromNode(root_node->children()[1])); - EXPECT_EQ(S_OK, child_node2->get_accHelp(SELF, helpText2.Receive())); - EXPECT_STREQ(L"name-from-title", helpText2); - - ScopedBstr helpText3; - ComPtr<IAccessible> child_node3( - IAccessibleFromNode(root_node->children()[2])); - EXPECT_EQ(S_OK, child_node3->get_accHelp(SELF, helpText3.Receive())); - EXPECT_STREQ(L"name-from-placeholder", helpText3); - - ScopedBstr helpText4; - ComPtr<IAccessible> child_node4( - IAccessibleFromNode(root_node->children()[3])); - EXPECT_EQ(S_OK, child_node4->get_accHelp(SELF, helpText4.Receive())); - EXPECT_STREQ(L"tooltip", helpText4); - - ScopedBstr helpText5; - ComPtr<IAccessible> child_node5( - IAccessibleFromNode(root_node->children()[4])); - EXPECT_EQ(S_FALSE, child_node5->get_accHelp(SELF, helpText5.Receive())); - - ScopedBstr helpText6; - ScopedVariant root_id(0); - EXPECT_EQ(S_OK, child_node4->get_accHelp(root_id, helpText6.Receive())); - EXPECT_STREQ(L"tooltip", helpText6); - - EXPECT_EQ(E_INVALIDARG, child_node5->get_accHelp(SELF, nullptr)); - ScopedVariant var_id(5); - EXPECT_EQ(E_INVALIDARG, - child_node5->get_accHelp(var_id, helpText5.Receive())); - ScopedVariant bad_id(999); - EXPECT_EQ(E_INVALIDARG, - child_node5->get_accHelp(bad_id, helpText5.Receive())); -} - TEST_F(AXPlatformNodeWinTest, TestIAccessibleSelectionListBoxOptionNothingSelected) { AXNodeData list; - list.id = 0; + list.id = 1; list.role = ax::mojom::Role::kListBox; AXNodeData list_item_1; - list_item_1.id = 1; + list_item_1.id = 2; list_item_1.role = ax::mojom::Role::kListBoxOption; list_item_1.SetName("Name1"); AXNodeData list_item_2; - list_item_2.id = 2; + list_item_2.id = 3; list_item_2.role = ax::mojom::Role::kListBoxOption; list_item_2.SetName("Name2"); @@ -669,17 +574,17 @@ TEST_F(AXPlatformNodeWinTest, TEST_F(AXPlatformNodeWinTest, TestIAccessibleSelectionListBoxOptionOneSelected) { AXNodeData list; - list.id = 0; + list.id = 1; list.role = ax::mojom::Role::kListBox; AXNodeData list_item_1; - list_item_1.id = 1; + list_item_1.id = 2; list_item_1.role = ax::mojom::Role::kListBoxOption; list_item_1.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); list_item_1.SetName("Name1"); AXNodeData list_item_2; - list_item_2.id = 2; + list_item_2.id = 3; list_item_2.role = ax::mojom::Role::kListBoxOption; list_item_2.SetName("Name2"); @@ -701,23 +606,23 @@ TEST_F(AXPlatformNodeWinTest, TEST_F(AXPlatformNodeWinTest, TestIAccessibleSelectionListBoxOptionMultipleSelected) { AXNodeData list; - list.id = 0; + list.id = 1; list.role = ax::mojom::Role::kListBox; AXNodeData list_item_1; - list_item_1.id = 1; + list_item_1.id = 2; list_item_1.role = ax::mojom::Role::kListBoxOption; list_item_1.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); list_item_1.SetName("Name1"); AXNodeData list_item_2; - list_item_2.id = 2; + list_item_2.id = 3; list_item_2.role = ax::mojom::Role::kListBoxOption; list_item_2.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); list_item_2.SetName("Name2"); AXNodeData list_item_3; - list_item_3.id = 3; + list_item_3.id = 4; list_item_3.role = ax::mojom::Role::kListBoxOption; list_item_3.SetName("Name3"); @@ -2937,14 +2842,14 @@ TEST_F(AXPlatformNodeWinTest, TestAnnotatedImageName) { TEST_F(AXPlatformNodeWinTest, TestIAccessibleTextGetNCharacters) { AXNodeData root; - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kStaticText; - root.child_ids.push_back(1); AXNodeData node; - node.id = 1; + node.id = 2; node.role = ax::mojom::Role::kStaticText; node.SetName("Name"); + root.child_ids.push_back(node.id); Init(root, node); @@ -3590,15 +3495,15 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetPropertySimple) { "role description"); root.AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 2); root.AddIntAttribute(ax::mojom::IntAttribute::kInvalidState, 1); - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kList; AXNodeData child1; - child1.id = 1; + child1.id = 2; child1.role = ax::mojom::Role::kListItem; child1.AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 1); child1.SetName("child1"); - root.child_ids.push_back(1); + root.child_ids.push_back(child1.id); Init(root, child1); @@ -3643,7 +3548,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetPropertySimple) { TEST_F(AXPlatformNodeWinTest, TestUIAGetPropertyValueClickablePoint) { AXNodeData root; - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kButton; root.relative_bounds.bounds = gfx::RectF(20, 30, 100, 200); Init(root); @@ -3660,7 +3565,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetPropertyValueClickablePoint) { TEST_F(AXPlatformNodeWinTest, TestUIAGetPropertyValue_Histogram) { AXNodeData root; - root.id = 0; + root.id = 1; Init(root); ComPtr<IRawElementProviderSimple> root_node = GetRootIRawElementProviderSimple(); @@ -3734,26 +3639,26 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetControllerForPropertyId) { TEST_F(AXPlatformNodeWinTest, TestUIAGetDescribedByPropertyId) { AXNodeData root; - std::vector<int32_t> describedby_ids = {1, 2, 3}; + std::vector<int32_t> describedby_ids = {2, 3, 4}; root.AddIntListAttribute(ax::mojom::IntListAttribute::kDescribedbyIds, describedby_ids); - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kMarquee; root.SetName("root"); AXNodeData child1; - child1.id = 1; + child1.id = 2; child1.role = ax::mojom::Role::kStaticText; child1.SetName("child1"); - root.child_ids.push_back(1); + root.child_ids.push_back(child1.id); AXNodeData child2; - child2.id = 2; + child2.id = 3; child2.role = ax::mojom::Role::kStaticText; child2.SetName("child2"); - root.child_ids.push_back(2); + root.child_ids.push_back(child2.id); Init(root, child1, child2); @@ -3835,25 +3740,25 @@ TEST_F(AXPlatformNodeWinTest, TestUIAItemStatusPropertyId) { TEST_F(AXPlatformNodeWinTest, TestUIAGetFlowsToPropertyId) { AXNodeData root; - std::vector<int32_t> flowto_ids = {1, 2, 3}; + std::vector<int32_t> flowto_ids = {2, 3, 4}; root.AddIntListAttribute(ax::mojom::IntListAttribute::kFlowtoIds, flowto_ids); - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kMarquee; root.SetName("root"); AXNodeData child1; - child1.id = 1; + child1.id = 2; child1.role = ax::mojom::Role::kStaticText; child1.SetName("child1"); - root.child_ids.push_back(1); + root.child_ids.push_back(child1.id); AXNodeData child2; - child2.id = 2; + child2.id = 3; child2.role = ax::mojom::Role::kStaticText; child2.SetName("child2"); - root.child_ids.push_back(2); + root.child_ids.push_back(child2.id); Init(root, child1, child2); @@ -3866,7 +3771,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetFlowsToPropertyId) { TEST_F(AXPlatformNodeWinTest, TestUIAGetPropertyValueFlowsFromNone) { AXNodeData root; - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kRootWebArea; root.SetName("root"); @@ -3884,16 +3789,16 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetPropertyValueFlowsFromNone) { TEST_F(AXPlatformNodeWinTest, TestUIAGetPropertyValueFlowsFromSingle) { AXNodeData root; - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kRootWebArea; root.SetName("root"); - root.AddIntListAttribute(ax::mojom::IntListAttribute::kFlowtoIds, {1}); + root.AddIntListAttribute(ax::mojom::IntListAttribute::kFlowtoIds, {2}); AXNodeData child1; - child1.id = 1; + child1.id = 2; child1.role = ax::mojom::Role::kGenericContainer; child1.SetName("child1"); - root.child_ids.push_back(1); + root.child_ids.push_back(child1.id); Init(root, child1); ASSERT_NE(nullptr, @@ -3909,23 +3814,23 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetPropertyValueFlowsFromSingle) { TEST_F(AXPlatformNodeWinTest, TestUIAGetPropertyValueFlowsFromMultiple) { AXNodeData root; - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kRootWebArea; root.SetName("root"); - root.AddIntListAttribute(ax::mojom::IntListAttribute::kFlowtoIds, {1, 2}); + root.AddIntListAttribute(ax::mojom::IntListAttribute::kFlowtoIds, {2, 3}); AXNodeData child1; - child1.id = 1; + child1.id = 2; child1.role = ax::mojom::Role::kGenericContainer; child1.SetName("child1"); - child1.AddIntListAttribute(ax::mojom::IntListAttribute::kFlowtoIds, {2}); - root.child_ids.push_back(1); + child1.AddIntListAttribute(ax::mojom::IntListAttribute::kFlowtoIds, {3}); + root.child_ids.push_back(child1.id); AXNodeData child2; - child2.id = 2; + child2.id = 3; child2.role = ax::mojom::Role::kGenericContainer; child2.SetName("child2"); - root.child_ids.push_back(2); + root.child_ids.push_back(child2.id); Init(root, child1, child2); ASSERT_NE(nullptr, @@ -3953,7 +3858,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetPropertyValueFlowsFromMultiple) { TEST_F(AXPlatformNodeWinTest, TestUIAGetPropertyValueFrameworkId) { AXNodeData root_ax_node_data; - root_ax_node_data.id = 0; + root_ax_node_data.id = 1; root_ax_node_data.role = ax::mojom::Role::kRootWebArea; Init(root_ax_node_data); @@ -4105,7 +4010,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetHostRawElementProvider) { TEST_F(AXPlatformNodeWinTest, TestUIAGetBoundingRectangle) { AXNodeData root_data; - root_data.id = 0; + root_data.id = 1; root_data.relative_bounds.bounds = gfx::RectF(10, 20, 30, 50); Init(root_data); @@ -4125,11 +4030,11 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetFragmentRoot) { // This test needs to be run on a child node since AXPlatformRootNodeWin // overrides the method. AXNodeData root_data; - root_data.id = 0; - root_data.child_ids.push_back(1); + root_data.id = 1; AXNodeData element1_data; - element1_data.id = 1; + element1_data.id = 2; + root_data.child_ids.push_back(element1_data.id); Init(root_data, element1_data); InitFragmentRoot(); @@ -4163,7 +4068,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetFragmentRoot) { TEST_F(AXPlatformNodeWinTest, TestUIAGetEmbeddedFragmentRoots) { AXNodeData root_data; - root_data.id = 0; + root_data.id = 1; Init(root_data); ComPtr<IRawElementProviderFragment> root_provider = @@ -4177,7 +4082,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetEmbeddedFragmentRoots) { TEST_F(AXPlatformNodeWinTest, TestUIAGetRuntimeId) { AXNodeData root_data; - root_data.id = 0; + root_data.id = 1; Init(root_data); ComPtr<IRawElementProviderFragment> root_provider = @@ -4207,7 +4112,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetRuntimeId) { TEST_F(AXPlatformNodeWinTest, TestUIAIWindowProviderGetIsModalUnset) { AXNodeData root; - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kRootWebArea; Init(root); @@ -4221,7 +4126,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIAIWindowProviderGetIsModalUnset) { TEST_F(AXPlatformNodeWinTest, TestUIAIWindowProviderGetIsModalFalse) { AXNodeData root; - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kRootWebArea; root.AddBoolAttribute(ax::mojom::BoolAttribute::kModal, false); Init(root); @@ -4240,7 +4145,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIAIWindowProviderGetIsModalFalse) { TEST_F(AXPlatformNodeWinTest, TestUIAIWindowProviderGetIsModalTrue) { AXNodeData root; - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kRootWebArea; root.AddBoolAttribute(ax::mojom::BoolAttribute::kModal, true); Init(root); @@ -4259,7 +4164,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIAIWindowProviderGetIsModalTrue) { TEST_F(AXPlatformNodeWinTest, TestUIAIWindowProviderInvalidArgument) { AXNodeData root; - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kRootWebArea; root.AddBoolAttribute(ax::mojom::BoolAttribute::kModal, true); Init(root); @@ -4282,7 +4187,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIAIWindowProviderInvalidArgument) { TEST_F(AXPlatformNodeWinTest, TestUIAIWindowProviderNotSupported) { AXNodeData root; - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kRootWebArea; root.AddBoolAttribute(ax::mojom::BoolAttribute::kModal, true); Init(root); @@ -4320,19 +4225,19 @@ TEST_F(AXPlatformNodeWinTest, TestUIAIWindowProviderNotSupported) { TEST_F(AXPlatformNodeWinTest, TestUIANavigate) { AXNodeData root_data; - root_data.id = 0; - root_data.child_ids.push_back(1); - root_data.child_ids.push_back(2); + root_data.id = 1; AXNodeData element1_data; - element1_data.id = 1; - element1_data.child_ids.push_back(3); + element1_data.id = 2; + root_data.child_ids.push_back(element1_data.id); AXNodeData element2_data; - element2_data.id = 2; + element2_data.id = 3; + root_data.child_ids.push_back(element2_data.id); AXNodeData element3_data; - element3_data.id = 3; + element3_data.id = 4; + element1_data.child_ids.push_back(element3_data.id); Init(root_data, element1_data, element2_data, element3_data); @@ -4544,29 +4449,29 @@ TEST_F(AXPlatformNodeWinTest, TEST_F(AXPlatformNodeWinTest, TestComputeUIAControlType) { AXNodeData root; - root.id = 0; + root.id = 1; root.role = ax::mojom::Role::kRootWebArea; AXNodeData child1; - int32_t child1_id = 1; + int32_t child1_id = 2; child1.id = child1_id; child1.role = ax::mojom::Role::kTable; root.child_ids.push_back(child1_id); AXNodeData child2; - int32_t child2_id = 2; + int32_t child2_id = 3; child2.id = child2_id; child2.role = ax::mojom::Role::kLayoutTable; root.child_ids.push_back(child2_id); AXNodeData child3; - int32_t child3_id = 3; + int32_t child3_id = 4; child3.id = child3_id; child3.role = ax::mojom::Role::kTextField; root.child_ids.push_back(child3_id); AXNodeData child4; - int32_t child4_id = 4; + int32_t child4_id = 5; child4.id = child4_id; child4.role = ax::mojom::Role::kSearchBox; root.child_ids.push_back(child4_id); @@ -4592,7 +4497,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIALandmarkType) { base::Optional<LONG> expected_landmark_type, const std::string& node_name = {}) { AXNodeData root_data; - root_data.id = 0; + root_data.id = 1; root_data.role = node_role; if (!node_name.empty()) root_data.SetName(node_name); @@ -4634,7 +4539,7 @@ TEST_F(AXPlatformNodeWinTest, TestUIALocalizedLandmarkType) { const std::wstring& expected_localized_landmark, const std::string& node_name = {}) { AXNodeData root_data; - root_data.id = 0; + root_data.id = 1; root_data.role = node_role; if (!node_name.empty()) root_data.SetName(node_name); @@ -4673,15 +4578,15 @@ TEST_F(AXPlatformNodeWinTest, TestUIALocalizedLandmarkType) { TEST_F(AXPlatformNodeWinTest, TestIRawElementProviderSimple2ShowContextMenu) { AXNodeData root_data; - root_data.id = 0; + root_data.id = 1; AXNodeData element1_data; - element1_data.id = 1; - root_data.child_ids.push_back(1); + element1_data.id = 2; + root_data.child_ids.push_back(element1_data.id); AXNodeData element2_data; - element2_data.id = 2; - root_data.child_ids.push_back(2); + element2_data.id = 3; + root_data.child_ids.push_back(element2_data.id); Init(root_data, element1_data, element2_data); @@ -4922,36 +4827,36 @@ TEST_F(AXPlatformNodeWinTest, TestUIAErrorHandling) { TEST_F(AXPlatformNodeWinTest, TestGetPatternProviderSupportedPatterns) { ui::AXNodeData root; - int32_t root_id = 0; + int32_t root_id = 1; root.id = root_id; root.role = ax::mojom::Role::kRootWebArea; ui::AXNodeData text_field_with_combo_box; - int32_t text_field_with_combo_box_id = 1; + int32_t text_field_with_combo_box_id = 2; text_field_with_combo_box.id = text_field_with_combo_box_id; text_field_with_combo_box.role = ax::mojom::Role::kTextFieldWithComboBox; root.child_ids.push_back(text_field_with_combo_box_id); ui::AXNodeData table; - int32_t table_id = 2; + int32_t table_id = 3; table.id = table_id; table.role = ax::mojom::Role::kTable; root.child_ids.push_back(table_id); ui::AXNodeData table_cell; - int32_t table_cell_id = 3; + int32_t table_cell_id = 4; table_cell.id = table_cell_id; table_cell.role = ax::mojom::Role::kCell; table.child_ids.push_back(table_cell_id); ui::AXNodeData meter; - int32_t meter_id = 4; + int32_t meter_id = 5; meter.id = meter_id; meter.role = ax::mojom::Role::kMeter; root.child_ids.push_back(meter_id); ui::AXNodeData group_with_scroll; - int32_t group_with_scroll_id = 5; + int32_t group_with_scroll_id = 6; group_with_scroll.id = group_with_scroll_id; group_with_scroll.role = ax::mojom::Role::kGroup; group_with_scroll.AddIntAttribute(ax::mojom::IntAttribute::kScrollXMin, 10); @@ -4960,25 +4865,25 @@ TEST_F(AXPlatformNodeWinTest, TestGetPatternProviderSupportedPatterns) { root.child_ids.push_back(group_with_scroll_id); ui::AXNodeData grid; - int32_t grid_id = 6; + int32_t grid_id = 7; grid.id = grid_id; grid.role = ax::mojom::Role::kGrid; root.child_ids.push_back(grid_id); ui::AXNodeData grid_cell; - int32_t grid_cell_id = 7; + int32_t grid_cell_id = 8; grid_cell.id = grid_cell_id; grid_cell.role = ax::mojom::Role::kCell; grid.child_ids.push_back(grid_cell_id); ui::AXNodeData checkbox; - int32_t checkbox_id = 8; + int32_t checkbox_id = 9; checkbox.id = checkbox_id; checkbox.role = ax::mojom::Role::kCheckBox; root.child_ids.push_back(checkbox_id); ui::AXNodeData link; - int32_t link_id = 9; + int32_t link_id = 10; link.id = link_id; link.role = ax::mojom::Role::kLink; root.child_ids.push_back(link_id); @@ -5031,7 +4936,7 @@ TEST_F(AXPlatformNodeWinTest, TestGetPatternProviderSupportedPatterns) { TEST_F(AXPlatformNodeWinTest, TestGetPatternProviderExpandCollapsePattern) { ui::AXNodeData root; - root.id = 0; + root.id = 1; ui::AXNodeData list_box; ui::AXNodeData list_item; @@ -5043,25 +4948,25 @@ TEST_F(AXPlatformNodeWinTest, TestGetPatternProviderExpandCollapsePattern) { ui::AXNodeData disclosure_triangle; ui::AXNodeData text_field_with_combo_box; - list_box.id = 1; - list_item.id = 2; - menu_item.id = 3; - menu_list_option.id = 4; - tree_item.id = 5; - combo_box_grouping.id = 6; - combo_box_menu_button.id = 7; - disclosure_triangle.id = 8; - text_field_with_combo_box.id = 9; - - root.child_ids.push_back(1); - root.child_ids.push_back(2); - root.child_ids.push_back(3); - root.child_ids.push_back(4); - root.child_ids.push_back(5); - root.child_ids.push_back(6); - root.child_ids.push_back(7); - root.child_ids.push_back(8); - root.child_ids.push_back(9); + list_box.id = 2; + list_item.id = 3; + menu_item.id = 4; + menu_list_option.id = 5; + tree_item.id = 6; + combo_box_grouping.id = 7; + combo_box_menu_button.id = 8; + disclosure_triangle.id = 9; + text_field_with_combo_box.id = 10; + + root.child_ids.push_back(list_box.id); + root.child_ids.push_back(list_item.id); + root.child_ids.push_back(menu_item.id); + root.child_ids.push_back(menu_list_option.id); + root.child_ids.push_back(tree_item.id); + root.child_ids.push_back(combo_box_grouping.id); + root.child_ids.push_back(combo_box_menu_button.id); + root.child_ids.push_back(disclosure_triangle.id); + root.child_ids.push_back(text_field_with_combo_box.id); // list_box HasPopup set to false, does not support expand collapse. list_box.role = ax::mojom::Role::kListBoxOption; @@ -5149,22 +5054,22 @@ TEST_F(AXPlatformNodeWinTest, TestGetPatternProviderExpandCollapsePattern) { TEST_F(AXPlatformNodeWinTest, TestGetPatternProviderInvokePattern) { ui::AXNodeData root; - root.id = 0; + root.id = 1; ui::AXNodeData link; ui::AXNodeData generic_container; ui::AXNodeData combo_box_grouping; ui::AXNodeData check_box; - link.id = 1; - generic_container.id = 2; - combo_box_grouping.id = 3; - check_box.id = 4; + link.id = 2; + generic_container.id = 3; + combo_box_grouping.id = 4; + check_box.id = 5; - root.child_ids.push_back(1); - root.child_ids.push_back(2); - root.child_ids.push_back(3); - root.child_ids.push_back(4); + root.child_ids.push_back(link.id); + root.child_ids.push_back(generic_container.id); + root.child_ids.push_back(combo_box_grouping.id); + root.child_ids.push_back(check_box.id); // Role link is clickable and neither supports expand collapse nor supports // toggle. It should support invoke pattern. @@ -5215,22 +5120,22 @@ TEST_F(AXPlatformNodeWinTest, TestGetPatternProviderInvokePattern) { TEST_F(AXPlatformNodeWinTest, TestIExpandCollapsePatternProviderAction) { ui::AXNodeData root; - root.id = 0; + root.id = 1; ui::AXNodeData combo_box_grouping_has_popup; ui::AXNodeData combo_box_grouping_expanded; ui::AXNodeData combo_box_grouping_collapsed; ui::AXNodeData combo_box_grouping_disabled; - combo_box_grouping_has_popup.id = 1; - combo_box_grouping_expanded.id = 2; - combo_box_grouping_collapsed.id = 3; - combo_box_grouping_disabled.id = 4; + combo_box_grouping_has_popup.id = 2; + combo_box_grouping_expanded.id = 3; + combo_box_grouping_collapsed.id = 4; + combo_box_grouping_disabled.id = 5; - root.child_ids.push_back(1); - root.child_ids.push_back(2); - root.child_ids.push_back(3); - root.child_ids.push_back(4); + root.child_ids.push_back(combo_box_grouping_has_popup.id); + root.child_ids.push_back(combo_box_grouping_expanded.id); + root.child_ids.push_back(combo_box_grouping_collapsed.id); + root.child_ids.push_back(combo_box_grouping_disabled.id); // combo_box_grouping HasPopup set to true, can collapse, can expand. // state is ExpandCollapseState_LeafNode. @@ -5310,16 +5215,16 @@ TEST_F(AXPlatformNodeWinTest, TestIExpandCollapsePatternProviderAction) { TEST_F(AXPlatformNodeWinTest, TestIInvokeProviderInvoke) { ui::AXNodeData root; - root.id = 0; + root.id = 1; ui::AXNodeData button; ui::AXNodeData button_disabled; - button.id = 1; - button_disabled.id = 2; + button.id = 2; + button_disabled.id = 3; - root.child_ids.push_back(1); - root.child_ids.push_back(2); + root.child_ids.push_back(button.id); + root.child_ids.push_back(button_disabled.id); // generic button can be invoked. button.role = ax::mojom::Role::kButton; diff --git a/chromium/ui/strings/translations/ui_strings_ru.xtb b/chromium/ui/strings/translations/ui_strings_ru.xtb index 5944f0e86be..07dccad0c51 100644 --- a/chromium/ui/strings/translations/ui_strings_ru.xtb +++ b/chromium/ui/strings/translations/ui_strings_ru.xtb @@ -75,7 +75,7 @@ <translation id="3234408098842461169">Стрелка вниз</translation> <translation id="3291688615589870984">{DAYS,plural, =1{1 день}one{# день}few{# дня}many{# дней}other{# дня}}</translation> <translation id="3295886253693811851">Позвонить:</translation> -<translation id="335581015389089642">Озвучить</translation> +<translation id="335581015389089642">Озвучивание</translation> <translation id="3443810440409579745">Вкладка получена.</translation> <translation id="3479552764303398839">Не сейчас</translation> <translation id="348799646910989694">Временное хранилище автоматически скрыто</translation> diff --git a/chromium/ui/webui/resources/cr_elements/cr_scrollable_behavior.js b/chromium/ui/webui/resources/cr_elements/cr_scrollable_behavior.js index 31d7934d36b..02ef35347c1 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_scrollable_behavior.js +++ b/chromium/ui/webui/resources/cr_elements/cr_scrollable_behavior.js @@ -79,30 +79,46 @@ const CrScrollableBehavior = { this.requestUpdateScroll(); - let nodeList = this.root.querySelectorAll('[scrollable] iron-list'); + const nodeList = this.root.querySelectorAll('[scrollable] iron-list'); if (!nodeList.length) { return; } + let nodesToResize = Array.from(nodeList).map(node => ({ + node: node, + lastScrollHeight: 0, + })); // Use setInterval to avoid initial render / sizing issues. - this.intervalId_ = window.setInterval(function() { - const unreadyNodes = []; - for (let i = 0; i < nodeList.length; i++) { - const node = nodeList[i]; - if (node.parentNode.scrollHeight == 0) { - unreadyNodes.push(node); - continue; + this.intervalId_ = window.setInterval(() => { + const checkAgain = []; + nodesToResize.forEach(({node, lastScrollHeight}) => { + const scrollHeight = node.parentNode.scrollHeight; + // A hidden scroll-container has a height of 0. When not hidden, it has + // a min-height of 1px and the iron-list needs a resize to show the + // initial items and update the |scrollHeight|. The initial item count + // is determined by the |scrollHeight|. A scrollHeight of 1px will + // result in the minimum default item count (currently 3). After the + // |scrollHeight| is updated to be greater than 1px, another resize is + // needed to correctly calculate the number of physical iron-list items + // to render. + if (scrollHeight != lastScrollHeight) { + const ironList = /** @type {!IronListElement} */ (node); + ironList.notifyResize(); } - const ironList = /** @type {!IronListElement} */ (node); - ironList.notifyResize(); - } - if (unreadyNodes.length == 0) { + if (scrollHeight <= 1) { + checkAgain.push({ + node: node, + lastScrollHeight: scrollHeight, + }); + } + }); + if (checkAgain.length == 0) { window.clearInterval(this.intervalId_); this.intervalId_ = null; } else { - nodeList = unreadyNodes; + nodesToResize = checkAgain; } - }.bind(this), 10); + }, 10); }, /** diff --git a/chromium/v8/include/v8-version.h b/chromium/v8/include/v8-version.h index 8c333c3e1ce..0406f65b08e 100644 --- a/chromium/v8/include/v8-version.h +++ b/chromium/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 7 #define V8_MINOR_VERSION 7 #define V8_BUILD_NUMBER 299 -#define V8_PATCH_LEVEL 8 +#define V8_PATCH_LEVEL 11 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/chromium/v8/src/builtins/base.tq b/chromium/v8/src/builtins/base.tq index 4aa1d578374..07af1f441f8 100644 --- a/chromium/v8/src/builtins/base.tq +++ b/chromium/v8/src/builtins/base.tq @@ -336,10 +336,16 @@ macro NewJSObject(implicit context: Context)(): JSObject { }; } +extern macro HasPrototypeSlot(JSFunction): bool; + macro GetDerivedMap(implicit context: Context)( target: JSFunction, newTarget: JSReceiver): Map { try { const constructor = Cast<JSFunction>(newTarget) otherwise SlowPath; + if (!HasPrototypeSlot(constructor)) { + goto SlowPath; + } + assert(IsConstructor(constructor)); const map = Cast<Map>(constructor.prototype_or_initial_map) otherwise SlowPath; if (LoadConstructorOrBackPointer(map) != target) { diff --git a/chromium/v8/src/codegen/code-stub-assembler.cc b/chromium/v8/src/codegen/code-stub-assembler.cc index 390746c27dc..e4f35ddcc88 100644 --- a/chromium/v8/src/codegen/code-stub-assembler.cc +++ b/chromium/v8/src/codegen/code-stub-assembler.cc @@ -2622,6 +2622,11 @@ TNode<BoolT> CodeStubAssembler::IsGeneratorFunction( Int32Constant(FunctionKind::kConciseGeneratorMethod)))); } +TNode<BoolT> CodeStubAssembler::HasPrototypeSlot(TNode<JSFunction> function) { + return TNode<BoolT>::UncheckedCast(IsSetWord32<Map::HasPrototypeSlotBit>( + LoadMapBitField(LoadMap(function)))); +} + TNode<BoolT> CodeStubAssembler::HasPrototypeProperty(TNode<JSFunction> function, TNode<Map> map) { // (has_prototype_slot() && IsConstructor()) || diff --git a/chromium/v8/src/codegen/code-stub-assembler.h b/chromium/v8/src/codegen/code-stub-assembler.h index 00a84c39265..47abd027490 100644 --- a/chromium/v8/src/codegen/code-stub-assembler.h +++ b/chromium/v8/src/codegen/code-stub-assembler.h @@ -1272,6 +1272,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler TNode<Map> LoadJSArrayElementsMap(SloppyTNode<Int32T> kind, SloppyTNode<Context> native_context); + TNode<BoolT> HasPrototypeSlot(TNode<JSFunction> function); TNode<BoolT> IsGeneratorFunction(TNode<JSFunction> function); TNode<BoolT> HasPrototypeProperty(TNode<JSFunction> function, TNode<Map> map); void GotoIfPrototypeRequiresRuntimeLookup(TNode<JSFunction> function, diff --git a/chromium/v8/src/compiler/js-native-context-specialization.cc b/chromium/v8/src/compiler/js-native-context-specialization.cc index 7d742a5f326..8f7552baa18 100644 --- a/chromium/v8/src/compiler/js-native-context-specialization.cc +++ b/chromium/v8/src/compiler/js-native-context-specialization.cc @@ -1060,7 +1060,8 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( Node* control = NodeProperties::GetControlInput(node); ZoneVector<PropertyAccessInfo> access_infos(zone()); - AccessInfoFactory access_info_factory(broker(), dependencies(), zone()); + AccessInfoFactory access_info_factory(broker(), dependencies(), + graph()->zone()); if (!access_info_factory.FinalizePropertyAccessInfos( feedback.access_infos(), access_mode, &access_infos)) { return NoChange(); @@ -1765,7 +1766,7 @@ Reduction JSNativeContextSpecialization::ReducePropertyAccess( if (name.has_value()) { ZoneVector<PropertyAccessInfo> access_infos(zone()); AccessInfoFactory access_info_factory(broker(), dependencies(), - zone()); + graph()->zone()); access_info_factory.ComputePropertyAccessInfos( receiver_maps, name->object(), access_mode, &access_infos); processed = new (zone()) NamedAccessFeedback(*name, access_infos); diff --git a/chromium/v8/src/inspector/custom-preview.cc b/chromium/v8/src/inspector/custom-preview.cc index f56562341ca..77cd6dc5f56 100644 --- a/chromium/v8/src/inspector/custom-preview.cc +++ b/chromium/v8/src/inspector/custom-preview.cc @@ -242,10 +242,10 @@ void bodyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { } // anonymous namespace void generateCustomPreview(int sessionId, const String16& groupName, - v8::Local<v8::Context> context, v8::Local<v8::Object> object, v8::MaybeLocal<v8::Value> maybeConfig, int maxDepth, std::unique_ptr<CustomPreview>* preview) { + v8::Local<v8::Context> context = object->CreationContext(); v8::Isolate* isolate = context->GetIsolate(); v8::MicrotasksScope microtasksScope(isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); diff --git a/chromium/v8/src/inspector/custom-preview.h b/chromium/v8/src/inspector/custom-preview.h index 1ae8e25a4c7..1e8c74a154c 100644 --- a/chromium/v8/src/inspector/custom-preview.h +++ b/chromium/v8/src/inspector/custom-preview.h @@ -13,9 +13,9 @@ namespace v8_inspector { const int kMaxCustomPreviewDepth = 20; void generateCustomPreview( - int sessionId, const String16& groupName, v8::Local<v8::Context> context, - v8::Local<v8::Object> object, v8::MaybeLocal<v8::Value> config, - int maxDepth, std::unique_ptr<protocol::Runtime::CustomPreview>* preview); + int sessionId, const String16& groupName, v8::Local<v8::Object> object, + v8::MaybeLocal<v8::Value> config, int maxDepth, + std::unique_ptr<protocol::Runtime::CustomPreview>* preview); } // namespace v8_inspector diff --git a/chromium/v8/src/inspector/injected-script.cc b/chromium/v8/src/inspector/injected-script.cc index 1edd559e4ef..ad91a8e65e9 100644 --- a/chromium/v8/src/inspector/injected-script.cc +++ b/chromium/v8/src/inspector/injected-script.cc @@ -458,7 +458,7 @@ Response InjectedScript::wrapObjectMirror( if (!response.isSuccess()) return response; if (customPreviewEnabled && value->IsObject()) { std::unique_ptr<protocol::Runtime::CustomPreview> customPreview; - generateCustomPreview(sessionId, groupName, context, value.As<v8::Object>(), + generateCustomPreview(sessionId, groupName, value.As<v8::Object>(), customPreviewConfig, maxCustomPreviewDepth, &customPreview); if (customPreview) (*result)->setCustomPreview(std::move(customPreview)); diff --git a/chromium/v8/src/regexp/regexp-compiler.cc b/chromium/v8/src/regexp/regexp-compiler.cc index c643f988c0f..c70bbc3e4a5 100644 --- a/chromium/v8/src/regexp/regexp-compiler.cc +++ b/chromium/v8/src/regexp/regexp-compiler.cc @@ -1970,9 +1970,11 @@ void ChoiceNode::GetQuickCheckDetails(QuickCheckDetails* details, } } +namespace { + // Check for [0-9A-Z_a-z]. -static void EmitWordCheck(RegExpMacroAssembler* assembler, Label* word, - Label* non_word, bool fall_through_on_word) { +void EmitWordCheck(RegExpMacroAssembler* assembler, Label* word, + Label* non_word, bool fall_through_on_word) { if (assembler->CheckSpecialCharacterClass( fall_through_on_word ? 'w' : 'W', fall_through_on_word ? non_word : word)) { @@ -1994,24 +1996,37 @@ static void EmitWordCheck(RegExpMacroAssembler* assembler, Label* word, // Emit the code to check for a ^ in multiline mode (1-character lookbehind // that matches newline or the start of input). -static void EmitHat(RegExpCompiler* compiler, RegExpNode* on_success, - Trace* trace) { +void EmitHat(RegExpCompiler* compiler, RegExpNode* on_success, Trace* trace) { RegExpMacroAssembler* assembler = compiler->macro_assembler(); - // We will be loading the previous character into the current character - // register. + + // We will load the previous character into the current character register. Trace new_trace(*trace); new_trace.InvalidateCurrentCharacter(); + // A positive (> 0) cp_offset means we've already successfully matched a + // non-empty-width part of the pattern, and thus cannot be at or before the + // start of the subject string. We can thus skip both at-start and + // bounds-checks when loading the one-character lookbehind. + const bool may_be_at_or_before_subject_string_start = + new_trace.cp_offset() <= 0; + Label ok; - if (new_trace.cp_offset() == 0) { - // The start of input counts as a newline in this context, so skip to - // ok if we are at the start. - assembler->CheckAtStart(&ok); + if (may_be_at_or_before_subject_string_start) { + // The start of input counts as a newline in this context, so skip to ok if + // we are at the start. + // TODO(jgruber): It would be less awkward to use CheckAtStart here, but + // that currently does not support a non-zero cp_offset. + Label not_at_start; + assembler->CheckNotAtStart(new_trace.cp_offset(), ¬_at_start); + assembler->GoTo(&ok); + assembler->Bind(¬_at_start); } - // We already checked that we are not at the start of input so it must be - // OK to load the previous character. + + // If we've already checked that we are not at the start of input, it's okay + // to load the previous character without bounds checks. + const bool can_skip_bounds_check = !may_be_at_or_before_subject_string_start; assembler->LoadCurrentCharacter(new_trace.cp_offset() - 1, - new_trace.backtrack(), false); + new_trace.backtrack(), can_skip_bounds_check); if (!assembler->CheckSpecialCharacterClass('n', new_trace.backtrack())) { // Newline means \n, \r, 0x2028 or 0x2029. if (!compiler->one_byte()) { @@ -2024,6 +2039,8 @@ static void EmitHat(RegExpCompiler* compiler, RegExpNode* on_success, on_success->Emit(compiler, &new_trace); } +} // namespace + // Emit the code to handle \b and \B (word-boundary or non-word-boundary). void AssertionNode::EmitBoundaryCheck(RegExpCompiler* compiler, Trace* trace) { RegExpMacroAssembler* assembler = compiler->macro_assembler(); @@ -2080,21 +2097,35 @@ void AssertionNode::BacktrackIfPrevious( Trace new_trace(*trace); new_trace.InvalidateCurrentCharacter(); - Label fall_through, dummy; - + Label fall_through; Label* non_word = backtrack_if_previous == kIsNonWord ? new_trace.backtrack() : &fall_through; Label* word = backtrack_if_previous == kIsNonWord ? &fall_through : new_trace.backtrack(); - if (new_trace.cp_offset() == 0) { + // A positive (> 0) cp_offset means we've already successfully matched a + // non-empty-width part of the pattern, and thus cannot be at or before the + // start of the subject string. We can thus skip both at-start and + // bounds-checks when loading the one-character lookbehind. + const bool may_be_at_or_before_subject_string_start = + new_trace.cp_offset() <= 0; + + if (may_be_at_or_before_subject_string_start) { // The start of input counts as a non-word character, so the question is // decided if we are at the start. - assembler->CheckAtStart(non_word); - } - // We already checked that we are not at the start of input so it must be - // OK to load the previous character. - assembler->LoadCurrentCharacter(new_trace.cp_offset() - 1, &dummy, false); + // TODO(jgruber): It would be less awkward to use CheckAtStart here, but + // that currently does not support a non-zero cp_offset. + Label not_at_start; + assembler->CheckNotAtStart(new_trace.cp_offset(), ¬_at_start); + assembler->GoTo(non_word); + assembler->Bind(¬_at_start); + } + + // If we've already checked that we are not at the start of input, it's okay + // to load the previous character without bounds checks. + const bool can_skip_bounds_check = !may_be_at_or_before_subject_string_start; + assembler->LoadCurrentCharacter(new_trace.cp_offset() - 1, non_word, + can_skip_bounds_check); EmitWordCheck(assembler, word, non_word, backtrack_if_previous == kIsNonWord); assembler->Bind(&fall_through); |