1 // Copyright 2016 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 CONTENT_BROWSER_BROWSER_ASSOCIATED_INTERFACE_H_
6 #define CONTENT_BROWSER_BROWSER_ASSOCIATED_INTERFACE_H_
7 
8 #include <string>
9 
10 #include "base/bind.h"
11 #include "base/macros.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/optional.h"
14 #include "content/common/content_export.h"
15 #include "content/public/browser/browser_message_filter.h"
16 #include "content/public/browser/browser_task_traits.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "ipc/ipc_channel_proxy.h"
19 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
20 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
21 
22 namespace content {
23 
24 // A helper interface which owns an associated interface receiver on the IO
25 // thread. Subclassess of BrowserMessageFilter may use this to simplify
26 // the transition to Mojo interfaces.
27 //
28 // In general the correct pattern for using this is as follows:
29 //
30 //   class FooMessageFilter : public BrowserMessageFilter,
31 //                            public BrowserAssociatedInterface<mojom::Foo>,
32 //                            public mojom::Foo {
33 //    public:
34 //     FooMessageFilter()
35 //         : BrowserMessageFilter(FooMsgStart),
36 //           BrowserAssociatedInterface<mojom::Foo>(this, this) {}
37 //
38 //     // BrowserMessageFilter implementation:
39 //     bool OnMessageReceived(const IPC::Message& message) override {
40 //       // ...
41 //       return true;
42 //     }
43 //
44 //     // mojom::Foo implementation:
45 //     void DoStuff() override { /* ... */ }
46 //   };
47 //
48 // The remote side of an IPC channel can request the |mojom::Foo| associated
49 // interface and use it would use any other associated remote proxy. Messages
50 // received for |mojom::Foo| on the local side of the channel will retain FIFO
51 // with respect to classical IPC messages received via OnMessageReceived().
52 //
53 // See BrowserAssociatedInterfaceTest.Basic for a simple working example usage.
54 template <typename Interface>
55 class BrowserAssociatedInterface {
56  public:
57   // |filter| and |impl| must live at least as long as this object.
BrowserAssociatedInterface(BrowserMessageFilter * filter,Interface * impl)58   BrowserAssociatedInterface(BrowserMessageFilter* filter, Interface* impl)
59       : internal_state_(new InternalState(impl)) {
60     filter->AddAssociatedInterface(
61         Interface::Name_,
62         base::BindRepeating(&InternalState::BindReceiver, internal_state_),
63         base::BindOnce(&InternalState::ClearReceivers, internal_state_));
64   }
65 
~BrowserAssociatedInterface()66   ~BrowserAssociatedInterface() { internal_state_->ClearReceivers(); }
67 
68  private:
69   friend class TestDriverMessageFilter;
70 
71   class InternalState : public base::RefCountedThreadSafe<InternalState> {
72    public:
InternalState(Interface * impl)73     explicit InternalState(Interface* impl)
74         : impl_(impl), receivers_(base::in_place) {}
75 
ClearReceivers()76     void ClearReceivers() {
77       if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
78         GetIOThreadTaskRunner({})->PostTask(
79             FROM_HERE, base::BindOnce(&InternalState::ClearReceivers, this));
80         return;
81       }
82       receivers_.reset();
83     }
84 
BindReceiver(mojo::ScopedInterfaceEndpointHandle handle)85     void BindReceiver(mojo::ScopedInterfaceEndpointHandle handle) {
86       DCHECK_CURRENTLY_ON(BrowserThread::IO);
87       // If this interface has already been shut down we drop the receiver.
88       if (!receivers_)
89         return;
90       receivers_->Add(
91           impl_, mojo::PendingAssociatedReceiver<Interface>(std::move(handle)));
92     }
93 
94    private:
95     friend class base::RefCountedThreadSafe<InternalState>;
96     friend class TestDriverMessageFilter;
97 
~InternalState()98     ~InternalState() {}
99 
100     Interface* impl_;
101     base::Optional<mojo::AssociatedReceiverSet<Interface>> receivers_;
102 
103     DISALLOW_COPY_AND_ASSIGN(InternalState);
104   };
105 
106   scoped_refptr<InternalState> internal_state_;
107 
108   DISALLOW_COPY_AND_ASSIGN(BrowserAssociatedInterface);
109 };
110 
111 }  // namespace content
112 
113 #endif  // CONTENT_BROWSER_BROWSER_ASSOCIATED_INTERFACE_H_
114