Prerender: Cancel prerendering if it requests audio output devices

This CL fixes the endless hanging bug that occurs when prerendering
documents executing `new AudioContext()`. The root cause of this bug
can be found in https://crbug.com/1233349.

It fixes this bug by introducing a new class named RestrictedModeCore to
RenderFrameAudioOutputStreamFactory. This class is used in replacement
with RenderFrameAudioOutputStreamFactory::Core and performs
message-level Mojo capability control during prerendering.

For normal pages, it doesn't change the current design.
For prerendering pages, a general workflow be like:
1.  [UI] RenderFrameHostImpl(RFHI) is requested to bind the receiver end
    of mojom::RendererAudioOutputStreamFactory.
2.  [UI] RFHI creates a new RenderFrameAudioOutputStreamFactory(RFAOSF)
    instance, and initialize it in the restricted mode.
3.  [UI] In the restricted mode, RFAOSF creates a RestrictedModeCore
    instance, and passes the receiver to it.
    Here, the behavior varies from a normal frame. In a normal frame,
    the receiver will be sent to the IO thread and will be bound with
    `core_`.
4.  [UI] RestrictedModeCore binds the receiver. It is responsible for
    handling Mojo messages during prerendering.
5.1 [UI] If the prerendering document requests for output devices:
    A message-level capability control is applied. Browser will cancel
    prerendering.
    Stop here.
    ********

5.2 If it doesn't: It can be activated later.
6.  [UI] RFHI knows that it is about to be activated. It informs
    RFAOSF by calling RFAOSF::ReleaseRestriction.
7.  [UI] RFAOSF asks RestrictedModeCore to unbind its receiver, and
    sends this receiver to the IO thread. core will bind the receiver
    there.
    From now on RestrictedModeCore no longer performs message control.
8.  [IO] Core binds the receiver. Now it can process Mojo messages.
    Everything is back to normal.

- Why Mojo capability control cannot cover this case:
  - We cannot cancel on binding RendererAudioOutputStreamFactory. This
    interface is requested for all frames regardless of whether it will
    be used.
  - We cannot defer it. One sync API(AudioContext()) relies on this
    interface and assumes the interface is always connected before
    using.
    Based on this assumption, it blocks the renderer's main thread when
    sending messages over this API. Deferring this API results in
    deadlock. See the issue description for more information.
  - We decide not to grant it for now. We may want to allow prerenders
    to request output devices and disallow them to create output streams
    after we gain a better understanding of audio APIs.

- Why the new class is needed.
  Otherwise it would be problematic if we want to check the prerendering
  state while processing Mojo messages. Without this class, the receiver
  will sent to the IO thread, while the main control flow of
  prerendering is running on the UI thread.

Bug: 1233349
Change-Id: I34a08643235a4ef285fef55fb9e20b90a8ae1b36
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3081002
Reviewed-by: Martin Šrámek <msramek@chromium.org>
Reviewed-by: Matt Falkenhagen <falken@chromium.org>
Reviewed-by: Takashi Toyoshima <toyoshim@chromium.org>
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Commit-Queue: Lingqi Chi <lingqi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#915906}
9 files changed
tree: 5f84f34e55b82cc00b737279d619a9f392f11ab2
  1. android_webview/
  2. apps/
  3. ash/
  4. base/
  5. build/
  6. build_overrides/
  7. buildtools/
  8. cc/
  9. chrome/
  10. chromecast/
  11. chromeos/
  12. cloud_print/
  13. codelabs/
  14. components/
  15. content/
  16. courgette/
  17. crypto/
  18. dbus/
  19. device/
  20. docs/
  21. extensions/
  22. fuchsia/
  23. gin/
  24. google_apis/
  25. google_update/
  26. gpu/
  27. headless/
  28. infra/
  29. ios/
  30. ipc/
  31. jingle/
  32. media/
  33. mojo/
  34. native_client_sdk/
  35. net/
  36. pdf/
  37. ppapi/
  38. printing/
  39. remoting/
  40. rlz/
  41. sandbox/
  42. services/
  43. skia/
  44. sql/
  45. storage/
  46. styleguide/
  47. testing/
  48. third_party/
  49. tools/
  50. ui/
  51. url/
  52. weblayer/
  53. .clang-format
  54. .clang-tidy
  55. .eslintrc.js
  56. .git-blame-ignore-revs
  57. .gitattributes
  58. .gitignore
  59. .gn
  60. .mailmap
  61. .vpython
  62. .vpython3
  63. .yapfignore
  64. AUTHORS
  65. BUILD.gn
  66. CODE_OF_CONDUCT.md
  67. codereview.settings
  68. DEPS
  69. DIR_METADATA
  70. ENG_REVIEW_OWNERS
  71. LICENSE
  72. LICENSE.chromium_os
  73. OWNERS
  74. PRESUBMIT.py
  75. PRESUBMIT_test.py
  76. PRESUBMIT_test_mocks.py
  77. README.md
  78. WATCHLISTS
README.md

Logo Chromium

Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all users to experience the web.

The project's web site is https://www.chromium.org.

To check out the source code locally, don't use git clone! Instead, follow the instructions on how to get the code.

Documentation in the source is rooted in docs/README.md.

Learn how to Get Around the Chromium Source Code Directory Structure .

For historical reasons, there are some small top level directories. Now the guidance is that new top level directories are for product (e.g. Chrome, Android WebView, Ash). Even if these products have multiple executables, the code should be in subdirectories of the product.

If you found a bug, please file it at https://crbug.com/new.