js_to_browser_messaging.cc 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/js_injection/browser/js_to_browser_messaging.h"
  5. #include "base/containers/contains.h"
  6. #include "base/memory/raw_ptr.h"
  7. #include "components/js_injection/browser/web_message.h"
  8. #include "components/js_injection/browser/web_message_host.h"
  9. #include "components/js_injection/browser/web_message_host_factory.h"
  10. #include "components/js_injection/browser/web_message_reply_proxy.h"
  11. #include "content/public/browser/disallow_activation_reason.h"
  12. #include "content/public/browser/render_process_host.h"
  13. #include "content/public/browser/web_contents.h"
  14. #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
  15. #include "third_party/blink/public/common/messaging/message_port_descriptor.h"
  16. #include "url/origin.h"
  17. #include "url/url_util.h"
  18. namespace js_injection {
  19. namespace {
  20. // We want to pass a string "null" for local file schemes, to make it
  21. // consistent to the Blink side SecurityOrigin serialization. When both
  22. // setAllow{File,Universal}AccessFromFileURLs are false, Blink::SecurityOrigin
  23. // will be serialized as string "null" for local file schemes, but when
  24. // setAllowFileAccessFromFileURLs is true, Blink::SecurityOrigin will be
  25. // serialized as the scheme, which will be inconsistentt to this place. In
  26. // this case we want to let developer to know that local files are not safe,
  27. // so we still pass "null".
  28. std::string GetOriginString(const url::Origin& source_origin) {
  29. return base::Contains(url::GetLocalSchemes(), source_origin.scheme())
  30. ? "null"
  31. : source_origin.Serialize();
  32. }
  33. } // namespace
  34. class JsToBrowserMessaging::ReplyProxyImpl : public WebMessageReplyProxy {
  35. public:
  36. ReplyProxyImpl(content::RenderFrameHost* render_frame_host,
  37. mojo::PendingAssociatedRemote<mojom::BrowserToJsMessaging>
  38. java_to_js_messaging)
  39. : render_frame_host_(render_frame_host),
  40. java_to_js_messaging_(std::move(java_to_js_messaging)) {}
  41. ReplyProxyImpl(const ReplyProxyImpl&) = delete;
  42. ReplyProxyImpl& operator=(const ReplyProxyImpl&) = delete;
  43. ~ReplyProxyImpl() override = default;
  44. // WebMessageReplyProxy:
  45. void PostWebMessage(std::unique_ptr<WebMessage> message) override {
  46. java_to_js_messaging_->OnPostMessage(message->message);
  47. }
  48. bool IsInBackForwardCache() override {
  49. return render_frame_host_->GetLifecycleState() ==
  50. content::RenderFrameHost::LifecycleState::kInBackForwardCache;
  51. }
  52. content::Page& GetPage() override { return render_frame_host_->GetPage(); }
  53. private:
  54. raw_ptr<content::RenderFrameHost> render_frame_host_;
  55. mojo::AssociatedRemote<mojom::BrowserToJsMessaging> java_to_js_messaging_;
  56. };
  57. JsToBrowserMessaging::JsToBrowserMessaging(
  58. content::RenderFrameHost* render_frame_host,
  59. mojo::PendingAssociatedReceiver<mojom::JsToBrowserMessaging> receiver,
  60. WebMessageHostFactory* factory,
  61. const OriginMatcher& origin_matcher)
  62. : render_frame_host_(render_frame_host),
  63. connection_factory_(factory),
  64. origin_matcher_(origin_matcher) {
  65. receiver_.Bind(std::move(receiver));
  66. }
  67. JsToBrowserMessaging::~JsToBrowserMessaging() = default;
  68. void JsToBrowserMessaging::OnBackForwardCacheStateChanged() {
  69. if (host_)
  70. host_->OnBackForwardCacheStateChanged();
  71. }
  72. void JsToBrowserMessaging::PostMessage(
  73. const std::u16string& message,
  74. std::vector<blink::MessagePortDescriptor> ports) {
  75. DCHECK(render_frame_host_);
  76. if (render_frame_host_->IsInactiveAndDisallowActivation(
  77. content::DisallowActivationReasonId::kJsInjectionPostMessage)) {
  78. return;
  79. }
  80. content::WebContents* web_contents =
  81. content::WebContents::FromRenderFrameHost(render_frame_host_);
  82. if (!web_contents)
  83. return;
  84. // |source_origin| has no race with this PostMessage call, because of
  85. // associated mojo channel, the committed origin message and PostMessage are
  86. // in sequence.
  87. const url::Origin source_origin =
  88. render_frame_host_->GetLastCommittedOrigin();
  89. if (!origin_matcher_.Matches(source_origin))
  90. return;
  91. // SetBrowserToJsMessaging must be called before this.
  92. DCHECK(reply_proxy_);
  93. if (!host_) {
  94. const std::string origin_string = GetOriginString(source_origin);
  95. const bool is_main_frame =
  96. web_contents->GetPrimaryMainFrame() == render_frame_host_;
  97. host_ = connection_factory_->CreateHost(origin_string, is_main_frame,
  98. reply_proxy_.get());
  99. #if DCHECK_IS_ON()
  100. origin_string_ = origin_string;
  101. is_main_frame_ = is_main_frame;
  102. #endif
  103. if (!host_)
  104. return;
  105. }
  106. // The origin and whether this is the main frame should not change once
  107. // PostMessage() has been received.
  108. #if DCHECK_IS_ON()
  109. DCHECK_EQ(GetOriginString(source_origin), origin_string_);
  110. DCHECK_EQ(is_main_frame_,
  111. web_contents->GetPrimaryMainFrame() == render_frame_host_);
  112. #endif
  113. std::unique_ptr<WebMessage> web_message = std::make_unique<WebMessage>();
  114. web_message->message = message;
  115. web_message->ports = std::move(ports);
  116. host_->OnPostMessage(std::move(web_message));
  117. }
  118. void JsToBrowserMessaging::SetBrowserToJsMessaging(
  119. mojo::PendingAssociatedRemote<mojom::BrowserToJsMessaging>
  120. java_to_js_messaging) {
  121. // TODO(https://crbug.com/1183557): this should really call
  122. // IsInactiveAndDisallowReactivation().
  123. // A RenderFrame may inject JsToBrowserMessaging in the JavaScript context
  124. // more than once because of reusing of RenderFrame.
  125. host_.reset();
  126. reply_proxy_ = std::make_unique<ReplyProxyImpl>(
  127. render_frame_host_, std::move(java_to_js_messaging));
  128. }
  129. } // namespace js_injection