1 // Copyright 2014 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 
5 #include "content/browser/permissions/permission_service_context.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "content/browser/permissions/permission_controller_impl.h"
11 #include "content/browser/permissions/permission_service_impl.h"
12 #include "content/public/browser/browser_context.h"
13 #include "content/public/browser/navigation_handle.h"
14 #include "content/public/browser/permission_controller.h"
15 #include "content/public/browser/render_frame_host.h"
16 #include "content/public/browser/render_process_host.h"
17 #include "content/public/browser/web_contents.h"
18 #include "mojo/public/cpp/bindings/remote.h"
19 
20 namespace content {
21 
22 class PermissionServiceContext::PermissionSubscription {
23  public:
PermissionSubscription(PermissionServiceContext * context,mojo::PendingRemote<blink::mojom::PermissionObserver> observer)24   PermissionSubscription(
25       PermissionServiceContext* context,
26       mojo::PendingRemote<blink::mojom::PermissionObserver> observer)
27       : context_(context), observer_(std::move(observer)) {
28     observer_.set_disconnect_handler(base::BindOnce(
29         &PermissionSubscription::OnConnectionError, base::Unretained(this)));
30   }
31   PermissionSubscription(const PermissionSubscription&) = delete;
32   PermissionSubscription& operator=(const PermissionSubscription&) = delete;
33 
~PermissionSubscription()34   ~PermissionSubscription() {
35     DCHECK_NE(id_, 0);
36     BrowserContext* browser_context = context_->GetBrowserContext();
37     if (browser_context) {
38       PermissionControllerImpl::FromBrowserContext(browser_context)
39           ->UnsubscribePermissionStatusChange(id_);
40     }
41   }
42 
OnConnectionError()43   void OnConnectionError() {
44     DCHECK_NE(id_, 0);
45     context_->ObserverHadConnectionError(id_);
46   }
47 
OnPermissionStatusChanged(blink::mojom::PermissionStatus status)48   void OnPermissionStatusChanged(blink::mojom::PermissionStatus status) {
49     observer_->OnPermissionStatusChange(status);
50   }
51 
set_id(int id)52   void set_id(int id) { id_ = id; }
53 
54  private:
55   PermissionServiceContext* const context_;
56   mojo::Remote<blink::mojom::PermissionObserver> observer_;
57   int id_ = 0;
58 };
59 
PermissionServiceContext(RenderFrameHost * render_frame_host)60 PermissionServiceContext::PermissionServiceContext(
61     RenderFrameHost* render_frame_host)
62     : WebContentsObserver(WebContents::FromRenderFrameHost(render_frame_host)),
63       render_frame_host_(render_frame_host),
64       render_process_host_(nullptr) {
65 }
66 
PermissionServiceContext(RenderProcessHost * render_process_host)67 PermissionServiceContext::PermissionServiceContext(
68     RenderProcessHost* render_process_host)
69     : WebContentsObserver(nullptr),
70       render_frame_host_(nullptr),
71       render_process_host_(render_process_host) {
72 }
73 
~PermissionServiceContext()74 PermissionServiceContext::~PermissionServiceContext() {
75 }
76 
CreateService(mojo::PendingReceiver<blink::mojom::PermissionService> receiver)77 void PermissionServiceContext::CreateService(
78     mojo::PendingReceiver<blink::mojom::PermissionService> receiver) {
79   DCHECK(render_frame_host_);
80   services_.Add(std::make_unique<PermissionServiceImpl>(
81                     this, render_frame_host_->GetLastCommittedOrigin()),
82                 std::move(receiver));
83 }
84 
CreateServiceForWorker(const url::Origin & origin,mojo::PendingReceiver<blink::mojom::PermissionService> receiver)85 void PermissionServiceContext::CreateServiceForWorker(
86     const url::Origin& origin,
87     mojo::PendingReceiver<blink::mojom::PermissionService> receiver) {
88   services_.Add(std::make_unique<PermissionServiceImpl>(this, origin),
89                 std::move(receiver));
90 }
91 
CreateSubscription(PermissionType permission_type,const url::Origin & origin,blink::mojom::PermissionStatus current_status,blink::mojom::PermissionStatus last_known_status,mojo::PendingRemote<blink::mojom::PermissionObserver> observer)92 void PermissionServiceContext::CreateSubscription(
93     PermissionType permission_type,
94     const url::Origin& origin,
95     blink::mojom::PermissionStatus current_status,
96     blink::mojom::PermissionStatus last_known_status,
97     mojo::PendingRemote<blink::mojom::PermissionObserver> observer) {
98   BrowserContext* browser_context = GetBrowserContext();
99   if (!browser_context)
100     return;
101 
102   auto subscription =
103       std::make_unique<PermissionSubscription>(this, std::move(observer));
104 
105   if (current_status != last_known_status) {
106     subscription->OnPermissionStatusChanged(current_status);
107     last_known_status = current_status;
108   }
109 
110   GURL requesting_origin(origin.Serialize());
111   int subscription_id =
112       PermissionControllerImpl::FromBrowserContext(browser_context)
113           ->SubscribePermissionStatusChange(
114               permission_type, render_frame_host_, requesting_origin,
115               base::BindRepeating(
116                   &PermissionSubscription::OnPermissionStatusChanged,
117                   base::Unretained(subscription.get())));
118   subscription->set_id(subscription_id);
119   subscriptions_[subscription_id] = std::move(subscription);
120 }
121 
ObserverHadConnectionError(int subscription_id)122 void PermissionServiceContext::ObserverHadConnectionError(int subscription_id) {
123   size_t erased = subscriptions_.erase(subscription_id);
124   DCHECK_EQ(1u, erased);
125 }
126 
RenderFrameHostChanged(RenderFrameHost * old_host,RenderFrameHost * new_host)127 void PermissionServiceContext::RenderFrameHostChanged(
128     RenderFrameHost* old_host,
129     RenderFrameHost* new_host) {
130   CloseBindings(old_host);
131 }
132 
FrameDeleted(RenderFrameHost * render_frame_host)133 void PermissionServiceContext::FrameDeleted(
134     RenderFrameHost* render_frame_host) {
135   CloseBindings(render_frame_host);
136 }
137 
DidFinishNavigation(NavigationHandle * navigation_handle)138 void PermissionServiceContext::DidFinishNavigation(
139     NavigationHandle* navigation_handle) {
140   if (!navigation_handle->HasCommitted() || navigation_handle->IsSameDocument())
141     return;
142 
143   CloseBindings(navigation_handle->GetRenderFrameHost());
144 }
145 
CloseBindings(RenderFrameHost * render_frame_host)146 void PermissionServiceContext::CloseBindings(
147     RenderFrameHost* render_frame_host) {
148   DCHECK(render_frame_host_);
149   if (render_frame_host != render_frame_host_)
150     return;
151 
152   services_.Clear();
153   subscriptions_.clear();
154 }
155 
GetBrowserContext() const156 BrowserContext* PermissionServiceContext::GetBrowserContext() const {
157   // web_contents() may return nullptr during teardown, or when showing
158   // an interstitial.
159   if (web_contents())
160     return web_contents()->GetBrowserContext();
161 
162   if (render_process_host_)
163     return render_process_host_->GetBrowserContext();
164 
165   return nullptr;
166 }
167 
GetEmbeddingOrigin() const168 GURL PermissionServiceContext::GetEmbeddingOrigin() const {
169   return web_contents() ? web_contents()->GetLastCommittedURL().GetOrigin()
170                         : GURL();
171 }
172 
173 }  // namespace content
174