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