• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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 #ifndef CEF_LIBCEF_BROWSER_FRAME_SERVICE_BASE_H_
6 #define CEF_LIBCEF_BROWSER_FRAME_SERVICE_BASE_H_
7 
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/threading/thread_checker.h"
13 #include "content/public/browser/navigation_handle.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/browser/web_contents.h"
16 #include "content/public/browser/web_contents_observer.h"
17 #include "mojo/public/cpp/bindings/pending_receiver.h"
18 #include "mojo/public/cpp/bindings/receiver.h"
19 #include "url/origin.h"
20 
21 namespace content {
22 
23 // Base class for mojo interface implementations tied to a document's lifetime.
24 // The service will be destroyed when any of the following happens:
25 // 1. mojo interface connection error happened,
26 // 2. the RenderFrameHost was deleted, or
27 // 3. navigation was committed on the RenderFrameHost (not same document) and
28 //    ShouldCloseOnFinishNavigation() returns true.
29 //
30 // WARNING: To avoid race conditions, subclasses MUST only get the origin via
31 // origin() instead of from |render_frame_host| passed in the constructor.
32 // See https://crbug.com/769189 for an example of such a race.
33 //
34 // Based on the old implementation of DocumentServiceBase that existed prior to
35 // https://crrev.com/2809effa24. CEF requires the old implementation to support
36 // bindings that outlive navigation.
37 template <typename Interface>
38 class FrameServiceBase : public Interface, public WebContentsObserver {
39  public:
FrameServiceBase(RenderFrameHost * render_frame_host,mojo::PendingReceiver<Interface> pending_receiver)40   FrameServiceBase(RenderFrameHost* render_frame_host,
41                    mojo::PendingReceiver<Interface> pending_receiver)
42       : WebContentsObserver(
43             WebContents::FromRenderFrameHost(render_frame_host)),
44         render_frame_host_(render_frame_host),
45         origin_(render_frame_host_->GetLastCommittedOrigin()),
46         receiver_(this, std::move(pending_receiver)) {
47     // |this| owns |receiver_|, so unretained is safe.
48     receiver_.set_disconnect_handler(
49         base::BindOnce(&FrameServiceBase::Close, base::Unretained(this)));
50   }
51 
52  protected:
53   // Make the destructor private since |this| can only be deleted by Close().
54   virtual ~FrameServiceBase() = default;
55 
56   // All subclasses should use this function to obtain the origin instead of
57   // trying to get it from the RenderFrameHost pointer directly.
origin()58   const url::Origin& origin() const { return origin_; }
59 
60   // Returns the RenderFrameHost held by this object.
render_frame_host()61   RenderFrameHost* render_frame_host() const { return render_frame_host_; }
62 
63   // Subclasses can use this to check thread safety.
64   // For example: DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
65   THREAD_CHECKER(thread_checker_);
66 
67  private:
68   // Disallow calling web_contents() directly from the subclasses to ensure that
69   // tab-level state doesn't get queried or updated when the RenderFrameHost is
70   // not active.
71   // Use WebContents::From(render_frame_host()) instead, but please keep in mind
72   // that the render_frame_host() might not be active. See
73   // RenderFrameHost::IsActive() for details.
74   using WebContentsObserver::web_contents;
75 
76   // WebContentsObserver implementation.
RenderFrameDeleted(RenderFrameHost * render_frame_host)77   void RenderFrameDeleted(RenderFrameHost* render_frame_host) final {
78     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
79 
80     if (render_frame_host == render_frame_host_) {
81       DVLOG(1) << __func__ << ": RenderFrame destroyed.";
82       Close();
83     }
84   }
85 
DidFinishNavigation(NavigationHandle * navigation_handle)86   void DidFinishNavigation(NavigationHandle* navigation_handle) final {
87     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
88     if (!ShouldCloseOnFinishNavigation())
89       return;
90 
91     if (!navigation_handle->HasCommitted() ||
92         navigation_handle->IsSameDocument() ||
93         navigation_handle->IsPageActivation()) {
94       return;
95     }
96 
97     if (navigation_handle->GetRenderFrameHost() == render_frame_host_) {
98       // FrameServiceBase is destroyed either when RenderFrameHost is
99       // destroyed (covered by RenderFrameDeleted) or when a new document
100       // commits in the same RenderFrameHost (covered by DidFinishNavigation).
101       // Only committed non-same-document non-bfcache non-prerendering
102       // activation navigations replace a document in existing RenderFrameHost.
103       DVLOG(1) << __func__ << ": Close connection on navigation.";
104       Close();
105     }
106   }
107 
108   // Used for CEF bindings that outlive navigation.
ShouldCloseOnFinishNavigation()109   virtual bool ShouldCloseOnFinishNavigation() const { return true; }
110 
111   // Stops observing WebContents and delete |this|.
Close()112   void Close() {
113     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
114     DVLOG(1) << __func__;
115     delete this;
116   }
117 
118   RenderFrameHost* const render_frame_host_ = nullptr;
119   const url::Origin origin_;
120   mojo::Receiver<Interface> receiver_;
121 };
122 
123 }  // namespace content
124 
125 #endif  // CEF_LIBCEF_BROWSER_FRAME_SERVICE_BASE_H_
126