OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 // | 4 // |
5 // Declaration of ATL module object and DLL exports. | 5 // Declaration of ATL module object and DLL exports. |
6 | 6 |
7 #include "base/at_exit.h" | 7 #include "base/at_exit.h" |
8 #include "base/atomic_ref_count.h" | 8 #include "base/atomic_ref_count.h" |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/logging_win.h" | 11 #include "base/logging_win.h" |
12 #include "base/thread.h" | |
13 #include "ceee/common/com_utils.h" | 12 #include "ceee/common/com_utils.h" |
14 #include "ceee/common/install_utils.h" | 13 #include "ceee/common/install_utils.h" |
15 #include "ceee/ie/broker/broker_rpc_client.h" | |
16 #include "ceee/ie/common/ceee_module_util.h" | 14 #include "ceee/ie/common/ceee_module_util.h" |
17 #include "ceee/ie/plugin/bho/browser_helper_object.h" | 15 #include "ceee/ie/plugin/bho/browser_helper_object.h" |
18 #include "ceee/ie/plugin/bho/executor.h" | 16 #include "ceee/ie/plugin/bho/executor.h" |
19 #include "ceee/ie/plugin/toolband/toolband_module_reporting.h" | 17 #include "ceee/ie/plugin/toolband/toolband_module_reporting.h" |
20 #include "ceee/ie/plugin/toolband/tool_band.h" | 18 #include "ceee/ie/plugin/toolband/tool_band.h" |
21 #include "ceee/ie/plugin/scripting/script_host.h" | 19 #include "ceee/ie/plugin/scripting/script_host.h" |
22 #include "ceee/common/windows_constants.h" | 20 #include "ceee/common/windows_constants.h" |
23 #include "chrome/common/url_constants.h" | 21 #include "chrome/common/url_constants.h" |
24 | 22 |
25 #include "toolband.h" // NOLINT | 23 #include "toolband.h" // NOLINT |
(...skipping 28 matching lines...) Expand all Loading... |
54 | 52 |
55 // Needed to make sure we call Init/Term outside the loader lock. | 53 // Needed to make sure we call Init/Term outside the loader lock. |
56 HRESULT DllCanUnloadNow(); | 54 HRESULT DllCanUnloadNow(); |
57 HRESULT DllGetClassObject(REFCLSID clsid, REFIID iid, void** object); | 55 HRESULT DllGetClassObject(REFCLSID clsid, REFIID iid, void** object); |
58 void Init(); | 56 void Init(); |
59 void Term(); | 57 void Term(); |
60 bool module_initialized() const { | 58 bool module_initialized() const { |
61 return module_initialized_; | 59 return module_initialized_; |
62 } | 60 } |
63 | 61 |
64 // Fires an event to the broker, so that the call can be made with an | 62 private: |
65 // instance of a broker proxy that was CoCreated in the worker thread. | |
66 void FireEventToBroker(const std::string& event_name, | |
67 const std::string& event_args); | |
68 | 63 |
69 private: | |
70 // TODO(vitalybuka@google.com): Fire events without this thread. | |
71 class ComWorkerThread : public base::Thread { | |
72 public: | |
73 ComWorkerThread(); | |
74 | |
75 // Called just prior to starting the message loop | |
76 virtual void Init(); | |
77 | |
78 // Called just after the message loop ends | |
79 virtual void CleanUp(); | |
80 | |
81 // Called by FireEventTask so that the broker we instantiate in the | |
82 // worker thread can be used. | |
83 void FireEventToBroker(BSTR event_name, BSTR event_args); | |
84 protected: | |
85 BrokerRpcClient broker_rpc_; | |
86 }; | |
87 | |
88 class FireEventTask : public Task { | |
89 public: | |
90 FireEventTask(ComWorkerThread* worker_thread, | |
91 const std::string& event_name, | |
92 const std::string& event_args) | |
93 : worker_thread_(worker_thread), | |
94 event_name_(event_name.c_str()), | |
95 event_args_(event_args.c_str()) { | |
96 } | |
97 virtual void Run() { | |
98 worker_thread_->FireEventToBroker(event_name_, event_args_); | |
99 } | |
100 private: | |
101 ComWorkerThread* worker_thread_; | |
102 CComBSTR event_name_; | |
103 CComBSTR event_args_; | |
104 }; | |
105 // We only start the thread on first use. If we would start it on | |
106 // initialization, when our DLL is loaded into the broker process, | |
107 // it would try to start this thread which tries to CoCreate a Broker | |
108 // and this could cause a complex deadlock... | |
109 void EnsureThreadStarted(); | |
110 | |
111 // We use a pointer so that we can make sure we only destroy the object | |
112 // when the thread is properly stopped. Otherwise, we would get a DCHECK | |
113 // if the thread is killed before we get to Stop it when DllCanUnloadNow | |
114 // returns S_OK, which happens when the application quits with live objects, | |
115 // this causes the destructor to DCHECK. | |
116 ComWorkerThread* worker_thread_; | |
117 base::AtExitManager at_exit_; | 64 base::AtExitManager at_exit_; |
118 bool module_initialized_; | 65 bool module_initialized_; |
119 bool crash_reporting_initialized_; | 66 bool crash_reporting_initialized_; |
120 | |
121 int worker_thread_ref_count_; | |
122 | |
123 friend void ceee_module_util::AddRefModuleWorkerThread(); | |
124 friend void ceee_module_util::ReleaseModuleWorkerThread(); | |
125 | |
126 void IncThreadRefCount(); | |
127 void DecThreadRefCount(); | |
128 }; | 67 }; |
129 | 68 |
130 ToolbandModule::ToolbandModule() | 69 ToolbandModule::ToolbandModule() |
131 : crash_reporting_initialized_(false), | 70 : crash_reporting_initialized_(false), |
132 module_initialized_(false), | 71 module_initialized_(false) { |
133 worker_thread_(NULL) { | |
134 wchar_t logfile_path[MAX_PATH]; | 72 wchar_t logfile_path[MAX_PATH]; |
135 DWORD len = ::GetTempPath(arraysize(logfile_path), logfile_path); | 73 DWORD len = ::GetTempPath(arraysize(logfile_path), logfile_path); |
136 ::PathAppend(logfile_path, kLogFileName); | 74 ::PathAppend(logfile_path, kLogFileName); |
137 | 75 |
138 // It seems we're obliged to initialize the current command line | 76 // It seems we're obliged to initialize the current command line |
139 // before initializing logging. This feels a little strange for | 77 // before initializing logging. This feels a little strange for |
140 // a plugin. | 78 // a plugin. |
141 CommandLine::Init(0, NULL); | 79 CommandLine::Init(0, NULL); |
142 | 80 |
143 logging::InitLogging( | 81 logging::InitLogging( |
(...skipping 11 matching lines...) Expand all Loading... |
155 | 93 |
156 // Needs to be called before we can use GURL. | 94 // Needs to be called before we can use GURL. |
157 chrome::RegisterChromeSchemes(); | 95 chrome::RegisterChromeSchemes(); |
158 | 96 |
159 ScriptHost::set_default_debug_application(&debug_application); | 97 ScriptHost::set_default_debug_application(&debug_application); |
160 } | 98 } |
161 | 99 |
162 ToolbandModule::~ToolbandModule() { | 100 ToolbandModule::~ToolbandModule() { |
163 ScriptHost::set_default_debug_application(NULL); | 101 ScriptHost::set_default_debug_application(NULL); |
164 | 102 |
165 // Just leave thread as is. Releasing interface from this thread may hang IE. | |
166 DCHECK(worker_thread_ref_count_ == 0); | |
167 DCHECK(worker_thread_ == NULL); | |
168 | |
169 // Uninitialize control hosting. | 103 // Uninitialize control hosting. |
170 BOOL uninitialized = AtlAxWinTerm(); | 104 BOOL uninitialized = AtlAxWinTerm(); |
171 DCHECK(uninitialized); | 105 DCHECK(uninitialized); |
172 | 106 |
173 logging::CloseLogFile(); | 107 logging::CloseLogFile(); |
174 } | 108 } |
175 | 109 |
176 HRESULT ToolbandModule::DllCanUnloadNow() { | 110 HRESULT ToolbandModule::DllCanUnloadNow() { |
177 HRESULT hr = CAtlDllModuleT<ToolbandModule>::DllCanUnloadNow(); | 111 HRESULT hr = CAtlDllModuleT<ToolbandModule>::DllCanUnloadNow(); |
178 if (hr == S_OK) { | 112 if (hr == S_OK) |
179 // We must protect our data member against concurrent calls to check if we | 113 Term(); |
180 // can be unloaded. We must also making the call to Term within the lock | |
181 // to make sure we don't try to re-initialize in case a new | |
182 // DllGetClassObject would occur in the mean time, in another thread. | |
183 m_csStaticDataInitAndTypeInfo.Lock(); | |
184 if (module_initialized_) { | |
185 Term(); | |
186 } | |
187 m_csStaticDataInitAndTypeInfo.Unlock(); | |
188 } | |
189 return hr; | 114 return hr; |
190 } | 115 } |
191 | 116 |
192 HRESULT ToolbandModule::DllGetClassObject(REFCLSID clsid, REFIID iid, | 117 HRESULT ToolbandModule::DllGetClassObject(REFCLSID clsid, REFIID iid, |
193 void** object) { | 118 void** object) { |
194 // Same comment as above in ToolbandModule::DllCanUnloadNow(). | 119 Init(); |
195 m_csStaticDataInitAndTypeInfo.Lock(); | |
196 if (!module_initialized_) { | |
197 Init(); | |
198 } | |
199 m_csStaticDataInitAndTypeInfo.Unlock(); | |
200 return CAtlDllModuleT<ToolbandModule>::DllGetClassObject(clsid, iid, object); | 120 return CAtlDllModuleT<ToolbandModule>::DllGetClassObject(clsid, iid, object); |
201 } | 121 } |
202 | 122 |
203 void ToolbandModule::Init() { | 123 void ToolbandModule::Init() { |
| 124 // We must protect our data member against concurrent calls to check if we |
| 125 // can be unloaded. We must also making the call to Term within the lock |
| 126 // to make sure we don't try to re-initialize in case a new |
| 127 // DllGetClassObject would occur in the mean time, in another thread. |
| 128 CComCritSecLock<CComCriticalSection> lock(m_csStaticDataInitAndTypeInfo); |
| 129 if (module_initialized_) |
| 130 return; |
204 crash_reporting_initialized_ = InitializeCrashReporting(); | 131 crash_reporting_initialized_ = InitializeCrashReporting(); |
205 module_initialized_ = true; | 132 module_initialized_ = true; |
206 } | 133 } |
207 | 134 |
208 void ToolbandModule::Term() { | 135 void ToolbandModule::Term() { |
209 if (worker_thread_ != NULL) { | 136 CComCritSecLock<CComCriticalSection> lock(m_csStaticDataInitAndTypeInfo); |
210 // It is OK to call Stop on a thread even when it isn't running. | 137 if (!module_initialized_) |
211 worker_thread_->Stop(); | 138 return; |
212 delete worker_thread_; | |
213 worker_thread_ = NULL; | |
214 } | |
215 if (crash_reporting_initialized_) { | 139 if (crash_reporting_initialized_) { |
216 bool crash_reporting_deinitialized = ShutdownCrashReporting(); | 140 bool crash_reporting_deinitialized = ShutdownCrashReporting(); |
217 DCHECK(crash_reporting_deinitialized); | 141 DCHECK(crash_reporting_deinitialized); |
218 crash_reporting_initialized_ = false; | 142 crash_reporting_initialized_ = false; |
219 } | 143 } |
220 module_initialized_ = false; | 144 module_initialized_ = false; |
221 } | 145 } |
222 | 146 |
223 void ToolbandModule::IncThreadRefCount() { | |
224 m_csStaticDataInitAndTypeInfo.Lock(); | |
225 DCHECK_GE(worker_thread_ref_count_, 0); | |
226 worker_thread_ref_count_++; | |
227 m_csStaticDataInitAndTypeInfo.Unlock(); | |
228 } | |
229 | |
230 void ToolbandModule::DecThreadRefCount() { | |
231 ComWorkerThread* thread = NULL; | |
232 | |
233 m_csStaticDataInitAndTypeInfo.Lock(); | |
234 // If we're already at 0, we have a problem, so we check if we're >=. | |
235 DCHECK_GT(worker_thread_ref_count_, 0); | |
236 | |
237 // If this was our last reference, we delete the thread. This is okay even if | |
238 // we increment the count again, because the thread is created on the "first" | |
239 // FireEventToBroker, thus it will be created again if needed. | |
240 if (--worker_thread_ref_count_ == 0) { | |
241 if (worker_thread_ != NULL) { | |
242 // Store the worker_thread to a temporary pointer. It will be freed later. | |
243 thread = worker_thread_; | |
244 worker_thread_ = NULL; | |
245 } | |
246 } | |
247 m_csStaticDataInitAndTypeInfo.Unlock(); | |
248 | |
249 // Clean the thread after the unlock to be certain we don't get a deadlock | |
250 // (the CriticalSection could be used in the worker thread). | |
251 if (thread) { | |
252 // It is OK to call Stop on a thread even when it isn't running. | |
253 thread->Stop(); | |
254 delete thread; | |
255 } | |
256 } | |
257 | |
258 void ToolbandModule::EnsureThreadStarted() { | |
259 m_csStaticDataInitAndTypeInfo.Lock(); | |
260 if (worker_thread_ == NULL) { | |
261 worker_thread_ = new ComWorkerThread; | |
262 // The COM worker thread must be a UI thread so that it can pump windows | |
263 // messages and allow COM to handle cross apartment calls. | |
264 worker_thread_->StartWithOptions(base::Thread::Options(MessageLoop::TYPE_UI, | |
265 0)); // stack_size | |
266 } | |
267 m_csStaticDataInitAndTypeInfo.Unlock(); | |
268 } | |
269 | |
270 void ToolbandModule::FireEventToBroker(const std::string& event_name, | |
271 const std::string& event_args) { | |
272 EnsureThreadStarted(); | |
273 DCHECK(worker_thread_ != NULL); | |
274 MessageLoop* message_loop = worker_thread_->message_loop(); | |
275 if (message_loop) { | |
276 message_loop->PostTask(FROM_HERE, | |
277 new FireEventTask(worker_thread_, event_name, event_args)); | |
278 } else { | |
279 LOG(ERROR) << "Trying to post a message before the COM worker thread is" | |
280 "completely initialized and ready."; | |
281 } | |
282 } | |
283 | |
284 | |
285 ToolbandModule::ComWorkerThread::ComWorkerThread() | |
286 : base::Thread("CEEE-COM Worker Thread") { | |
287 } | |
288 | |
289 void ToolbandModule::ComWorkerThread::Init() { | |
290 ::CoInitializeEx(0, COINIT_MULTITHREADED); | |
291 // TODO(vitalybuka@google.com): Start broker without COM. | |
292 CComPtr<ICeeeBroker> broker_; | |
293 HRESULT hr = broker_.CoCreateInstance(CLSID_CeeeBroker); | |
294 DCHECK(SUCCEEDED(hr)) << "Failed to create broker. " << com::LogHr(hr); | |
295 broker_rpc_.Connect(); | |
296 DCHECK(broker_rpc_.is_connected()); | |
297 ::CoUninitialize(); | |
298 } | |
299 | |
300 void ToolbandModule::ComWorkerThread::CleanUp() { | |
301 broker_rpc_.Disconnect(); | |
302 } | |
303 | |
304 void ToolbandModule::ComWorkerThread::FireEventToBroker(BSTR event_name, | |
305 BSTR event_args) { | |
306 DCHECK(broker_rpc_.is_connected()); | |
307 bool result = broker_rpc_.FireEvent(event_name, event_args); | |
308 DCHECK(result); | |
309 } | |
310 | |
311 ToolbandModule module; | 147 ToolbandModule module; |
312 | 148 |
313 void ceee_module_util::AddRefModuleWorkerThread() { | |
314 module.IncThreadRefCount(); | |
315 } | |
316 void ceee_module_util::ReleaseModuleWorkerThread() { | |
317 module.DecThreadRefCount(); | |
318 } | |
319 | |
320 void ceee_module_util::FireEventToBroker(const std::string& event_name, | |
321 const std::string& event_args) { | |
322 module.FireEventToBroker(event_name, event_args); | |
323 } | |
324 | |
325 void ceee_module_util::Lock() { | 149 void ceee_module_util::Lock() { |
326 module.m_csStaticDataInitAndTypeInfo.Lock(); | 150 module.m_csStaticDataInitAndTypeInfo.Lock(); |
327 } | 151 } |
328 | 152 |
329 void ceee_module_util::Unlock() { | 153 void ceee_module_util::Unlock() { |
330 module.m_csStaticDataInitAndTypeInfo.Unlock(); | 154 module.m_csStaticDataInitAndTypeInfo.Unlock(); |
331 } | 155 } |
332 | 156 |
333 LONG ceee_module_util::LockModule() { | 157 LONG ceee_module_util::LockModule() { |
334 return module.Lock(); | 158 return module.Lock(); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
378 } | 202 } |
379 | 203 |
380 CEEE_DEFINE_DLL_REGISTER_SERVER() | 204 CEEE_DEFINE_DLL_REGISTER_SERVER() |
381 | 205 |
382 // DllUnregisterServer - Removes entries from the system registry | 206 // DllUnregisterServer - Removes entries from the system registry |
383 STDAPI DllUnregisterServer(void) { | 207 STDAPI DllUnregisterServer(void) { |
384 // We always allow unregistration, even if no --enable-ceee install flag. | 208 // We always allow unregistration, even if no --enable-ceee install flag. |
385 HRESULT hr = module.DllUnregisterServer(); | 209 HRESULT hr = module.DllUnregisterServer(); |
386 return hr; | 210 return hr; |
387 } | 211 } |
OLD | NEW |