Chromium Code Reviews| 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" | 14 #include "ceee/ie/broker/broker_rpc_client.h" |
| 16 #include "ceee/ie/common/ceee_module_util.h" | 15 #include "ceee/ie/common/ceee_module_util.h" |
| 17 #include "ceee/ie/plugin/bho/browser_helper_object.h" | 16 #include "ceee/ie/plugin/bho/browser_helper_object.h" |
| 18 #include "ceee/ie/plugin/bho/executor.h" | 17 #include "ceee/ie/plugin/bho/executor.h" |
| 19 #include "ceee/ie/plugin/toolband/toolband_module_reporting.h" | 18 #include "ceee/ie/plugin/toolband/toolband_module_reporting.h" |
| 20 #include "ceee/ie/plugin/toolband/tool_band.h" | 19 #include "ceee/ie/plugin/toolband/tool_band.h" |
| 21 #include "ceee/ie/plugin/scripting/script_host.h" | 20 #include "ceee/ie/plugin/scripting/script_host.h" |
| 22 #include "ceee/common/windows_constants.h" | 21 #include "ceee/common/windows_constants.h" |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 54 | 53 |
| 55 // Needed to make sure we call Init/Term outside the loader lock. | 54 // Needed to make sure we call Init/Term outside the loader lock. |
| 56 HRESULT DllCanUnloadNow(); | 55 HRESULT DllCanUnloadNow(); |
| 57 HRESULT DllGetClassObject(REFCLSID clsid, REFIID iid, void** object); | 56 HRESULT DllGetClassObject(REFCLSID clsid, REFIID iid, void** object); |
| 58 void Init(); | 57 void Init(); |
| 59 void Term(); | 58 void Term(); |
| 60 bool module_initialized() const { | 59 bool module_initialized() const { |
| 61 return module_initialized_; | 60 return module_initialized_; |
| 62 } | 61 } |
| 63 | 62 |
| 64 // Fires an event to the broker, so that the call can be made with an | 63 // Fires an event to the broker. |
| 65 // instance of a broker proxy that was CoCreated in the worker thread. | |
| 66 void FireEventToBroker(const std::string& event_name, | 64 void FireEventToBroker(const std::string& event_name, |
| 67 const std::string& event_args); | 65 const std::string& event_args); |
| 68 | 66 |
| 69 private: | 67 private: |
| 70 // TODO(vitalybuka@google.com): Fire events without this thread. | |
| 71 class ComWorkerThread : public base::Thread { | |
| 72 public: | |
| 73 ComWorkerThread(); | |
| 74 | 68 |
| 75 // Called just prior to starting the message loop | 69 // We only attach broker on first use. |
| 76 virtual void Init(); | 70 void EnsureBrokerIsConnected(); |
| 77 | 71 |
| 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_; | 72 base::AtExitManager at_exit_; |
| 118 bool module_initialized_; | 73 bool module_initialized_; |
| 119 bool crash_reporting_initialized_; | 74 bool crash_reporting_initialized_; |
| 120 | 75 |
| 121 int worker_thread_ref_count_; | 76 int broker_ref_count_; |
| 122 | 77 |
| 123 friend void ceee_module_util::AddRefModuleWorkerThread(); | 78 BrokerRpcClient broker_rpc_client_; |
| 124 friend void ceee_module_util::ReleaseModuleWorkerThread(); | |
| 125 | 79 |
| 126 void IncThreadRefCount(); | 80 friend void ceee_module_util::AddRefBroker(); |
| 127 void DecThreadRefCount(); | 81 friend void ceee_module_util::ReleaseBroker(); |
| 82 | |
| 83 void IncBrokerRefCount(); | |
| 84 void DecBrokerRefCount(); | |
| 128 }; | 85 }; |
| 129 | 86 |
| 130 ToolbandModule::ToolbandModule() | 87 ToolbandModule::ToolbandModule() |
| 131 : crash_reporting_initialized_(false), | 88 : crash_reporting_initialized_(false), |
| 132 module_initialized_(false), | 89 module_initialized_(false) { |
| 133 worker_thread_(NULL) { | |
| 134 wchar_t logfile_path[MAX_PATH]; | 90 wchar_t logfile_path[MAX_PATH]; |
| 135 DWORD len = ::GetTempPath(arraysize(logfile_path), logfile_path); | 91 DWORD len = ::GetTempPath(arraysize(logfile_path), logfile_path); |
| 136 ::PathAppend(logfile_path, kLogFileName); | 92 ::PathAppend(logfile_path, kLogFileName); |
| 137 | 93 |
| 138 // It seems we're obliged to initialize the current command line | 94 // It seems we're obliged to initialize the current command line |
| 139 // before initializing logging. This feels a little strange for | 95 // before initializing logging. This feels a little strange for |
| 140 // a plugin. | 96 // a plugin. |
| 141 CommandLine::Init(0, NULL); | 97 CommandLine::Init(0, NULL); |
| 142 | 98 |
| 143 logging::InitLogging( | 99 logging::InitLogging( |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 155 | 111 |
| 156 // Needs to be called before we can use GURL. | 112 // Needs to be called before we can use GURL. |
| 157 chrome::RegisterChromeSchemes(); | 113 chrome::RegisterChromeSchemes(); |
| 158 | 114 |
| 159 ScriptHost::set_default_debug_application(&debug_application); | 115 ScriptHost::set_default_debug_application(&debug_application); |
| 160 } | 116 } |
| 161 | 117 |
| 162 ToolbandModule::~ToolbandModule() { | 118 ToolbandModule::~ToolbandModule() { |
| 163 ScriptHost::set_default_debug_application(NULL); | 119 ScriptHost::set_default_debug_application(NULL); |
| 164 | 120 |
| 165 // Just leave thread as is. Releasing interface from this thread may hang IE. | 121 DCHECK(!broker_rpc_client_.is_connected()); |
| 166 DCHECK(worker_thread_ref_count_ == 0); | |
| 167 DCHECK(worker_thread_ == NULL); | |
| 168 | 122 |
| 169 // Uninitialize control hosting. | 123 // Uninitialize control hosting. |
| 170 BOOL uninitialized = AtlAxWinTerm(); | 124 BOOL uninitialized = AtlAxWinTerm(); |
| 171 DCHECK(uninitialized); | 125 DCHECK(uninitialized); |
| 172 | 126 |
| 173 logging::CloseLogFile(); | 127 logging::CloseLogFile(); |
| 174 } | 128 } |
| 175 | 129 |
| 176 HRESULT ToolbandModule::DllCanUnloadNow() { | 130 HRESULT ToolbandModule::DllCanUnloadNow() { |
| 177 HRESULT hr = CAtlDllModuleT<ToolbandModule>::DllCanUnloadNow(); | 131 HRESULT hr = CAtlDllModuleT<ToolbandModule>::DllCanUnloadNow(); |
| 178 if (hr == S_OK) { | 132 if (hr == S_OK) |
| 179 // We must protect our data member against concurrent calls to check if we | 133 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; | 134 return hr; |
| 190 } | 135 } |
| 191 | 136 |
| 192 HRESULT ToolbandModule::DllGetClassObject(REFCLSID clsid, REFIID iid, | 137 HRESULT ToolbandModule::DllGetClassObject(REFCLSID clsid, REFIID iid, |
| 193 void** object) { | 138 void** object) { |
| 194 // Same comment as above in ToolbandModule::DllCanUnloadNow(). | 139 Init(); |
| 195 m_csStaticDataInitAndTypeInfo.Lock(); | |
| 196 if (!module_initialized_) { | |
| 197 Init(); | |
| 198 } | |
| 199 m_csStaticDataInitAndTypeInfo.Unlock(); | |
| 200 return CAtlDllModuleT<ToolbandModule>::DllGetClassObject(clsid, iid, object); | 140 return CAtlDllModuleT<ToolbandModule>::DllGetClassObject(clsid, iid, object); |
| 201 } | 141 } |
| 202 | 142 |
| 203 void ToolbandModule::Init() { | 143 void ToolbandModule::Init() { |
| 144 // We must protect our data member against concurrent calls to check if we | |
| 145 // can be unloaded. We must also making the call to Term within the lock | |
| 146 // to make sure we don't try to re-initialize in case a new | |
| 147 // DllGetClassObject would occur in the mean time, in another thread. | |
| 148 CComCritSecLock<CComCriticalSection> lock(m_csStaticDataInitAndTypeInfo); | |
| 149 if (module_initialized_) | |
| 150 return; | |
| 204 crash_reporting_initialized_ = InitializeCrashReporting(); | 151 crash_reporting_initialized_ = InitializeCrashReporting(); |
| 205 module_initialized_ = true; | 152 module_initialized_ = true; |
| 206 } | 153 } |
| 207 | 154 |
| 208 void ToolbandModule::Term() { | 155 void ToolbandModule::Term() { |
| 209 if (worker_thread_ != NULL) { | 156 CComCritSecLock<CComCriticalSection> lock(m_csStaticDataInitAndTypeInfo); |
| 210 // It is OK to call Stop on a thread even when it isn't running. | 157 if (!module_initialized_) |
| 211 worker_thread_->Stop(); | 158 return; |
| 212 delete worker_thread_; | 159 if (broker_rpc_client_.is_connected()) |
| 213 worker_thread_ = NULL; | 160 broker_rpc_client_.Disconnect(); |
| 214 } | 161 |
| 215 if (crash_reporting_initialized_) { | 162 if (crash_reporting_initialized_) { |
| 216 bool crash_reporting_deinitialized = ShutdownCrashReporting(); | 163 bool crash_reporting_deinitialized = ShutdownCrashReporting(); |
| 217 DCHECK(crash_reporting_deinitialized); | 164 DCHECK(crash_reporting_deinitialized); |
| 218 crash_reporting_initialized_ = false; | 165 crash_reporting_initialized_ = false; |
| 219 } | 166 } |
| 220 module_initialized_ = false; | 167 module_initialized_ = false; |
| 221 } | 168 } |
| 222 | 169 |
| 223 void ToolbandModule::IncThreadRefCount() { | 170 void ToolbandModule::IncBrokerRefCount() { |
| 224 m_csStaticDataInitAndTypeInfo.Lock(); | 171 CComCritSecLock<CComCriticalSection> lock(m_csStaticDataInitAndTypeInfo); |
| 225 DCHECK_GE(worker_thread_ref_count_, 0); | 172 DCHECK_GE(broker_ref_count_, 0); |
| 226 worker_thread_ref_count_++; | 173 broker_ref_count_++; |
| 227 m_csStaticDataInitAndTypeInfo.Unlock(); | |
| 228 } | 174 } |
| 229 | 175 |
| 230 void ToolbandModule::DecThreadRefCount() { | 176 void ToolbandModule::DecBrokerRefCount() { |
| 231 ComWorkerThread* thread = NULL; | 177 CComCritSecLock<CComCriticalSection> lock(m_csStaticDataInitAndTypeInfo); |
| 178 // If we're already at 0, we have a problem, so we check if we're >=. | |
| 179 DCHECK_GT(broker_ref_count_, 0); | |
| 232 | 180 |
| 233 m_csStaticDataInitAndTypeInfo.Lock(); | 181 // If this was our last reference, we disconnect broker. This is okay even if |
| 234 // If we're already at 0, we have a problem, so we check if we're >=. | 182 // we increment the count again, because the broker is created on the "first" |
| 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. | 183 // FireEventToBroker, thus it will be created again if needed. |
| 240 if (--worker_thread_ref_count_ == 0) { | 184 if (--broker_ref_count_ == 0) { |
| 241 if (worker_thread_ != NULL) { | 185 if (broker_rpc_client_.is_connected()) |
| 242 // Store the worker_thread to a temporary pointer. It will be freed later. | 186 broker_rpc_client_.Disconnect(); |
| 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 } | 187 } |
| 256 } | 188 } |
| 257 | 189 |
| 258 void ToolbandModule::EnsureThreadStarted() { | 190 void ToolbandModule::EnsureBrokerIsConnected() { |
| 259 m_csStaticDataInitAndTypeInfo.Lock(); | 191 CComCritSecLock<CComCriticalSection> lock(m_csStaticDataInitAndTypeInfo); |
| 260 if (worker_thread_ == NULL) { | 192 if (!broker_rpc_client_.is_connected()) { |
| 261 worker_thread_ = new ComWorkerThread; | 193 ::CoInitializeEx(0, COINIT_MULTITHREADED); |
|
Sigurður Ásgeirsson
2010/11/16 18:23:04
I think we can skip the Coinit calls. We're runnin
Vitaly Buka corp
2010/11/18 23:14:21
Done.
| |
| 262 // The COM worker thread must be a UI thread so that it can pump windows | 194 // TODO(vitalybuka@google.com): Start broker without COM after the last |
| 263 // messages and allow COM to handle cross apartment calls. | 195 // COM interface is removed. |
| 264 worker_thread_->StartWithOptions(base::Thread::Options(MessageLoop::TYPE_UI, | 196 CComPtr<ICeeeBrokerRegistrar> broker; |
| 265 0)); // stack_size | 197 HRESULT hr = broker.CoCreateInstance(CLSID_CeeeBroker); |
| 198 DCHECK(SUCCEEDED(hr)) << "Failed to create broker. " << com::LogHr(hr); | |
| 199 broker_rpc_client_.Connect(); | |
|
Sigurður Ásgeirsson
2010/11/16 18:23:04
test return value and LOG(ERROR) here?
Vitaly Buka corp
2010/11/18 23:14:21
Done.
| |
| 200 DCHECK(broker_rpc_client_.is_connected()); | |
| 201 ::CoUninitialize(); | |
|
Sigurður Ásgeirsson
2010/11/16 18:23:04
Ooof.
If the ::Coinitialize call fails (e.g. due t
Vitaly Buka corp
2010/11/18 23:14:21
Done.
| |
| 266 } | 202 } |
| 267 m_csStaticDataInitAndTypeInfo.Unlock(); | |
| 268 } | 203 } |
| 269 | 204 |
| 270 void ToolbandModule::FireEventToBroker(const std::string& event_name, | 205 void ToolbandModule::FireEventToBroker(const std::string& event_name, |
| 271 const std::string& event_args) { | 206 const std::string& event_args) { |
| 272 EnsureThreadStarted(); | 207 EnsureBrokerIsConnected(); |
| 273 DCHECK(worker_thread_ != NULL); | 208 CComBSTR name(event_name.c_str()); |
|
Sigurður Ásgeirsson
2010/11/16 18:23:04
This looks like needless conversion and allocation
Vitaly Buka corp
2010/11/18 23:14:21
Done.
| |
| 274 MessageLoop* message_loop = worker_thread_->message_loop(); | 209 CComBSTR args(event_args.c_str()); |
| 275 if (message_loop) { | 210 LOG_IF(ERROR, broker_rpc_client_.FireEvent(name, args)) << |
|
Sigurður Ásgeirsson
2010/11/16 18:23:04
Fire event returns true on error?
I don't like hid
Vitaly Buka corp
2010/11/18 23:14:21
Done.
| |
| 276 message_loop->PostTask(FROM_HERE, | 211 "Failed to fire event to broker."; |
| 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 } | 212 } |
| 283 | 213 |
| 284 | 214 |
| 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; | 215 ToolbandModule module; |
| 312 | 216 |
| 313 void ceee_module_util::AddRefModuleWorkerThread() { | 217 void ceee_module_util::AddRefBroker() { |
| 314 module.IncThreadRefCount(); | 218 module.IncBrokerRefCount(); |
| 315 } | 219 } |
| 316 void ceee_module_util::ReleaseModuleWorkerThread() { | 220 void ceee_module_util::ReleaseBroker() { |
| 317 module.DecThreadRefCount(); | 221 module.DecBrokerRefCount(); |
| 318 } | 222 } |
| 319 | 223 |
| 320 void ceee_module_util::FireEventToBroker(const std::string& event_name, | 224 void ceee_module_util::FireEventToBroker(const std::string& event_name, |
| 321 const std::string& event_args) { | 225 const std::string& event_args) { |
| 322 module.FireEventToBroker(event_name, event_args); | 226 module.FireEventToBroker(event_name, event_args); |
| 323 } | 227 } |
| 324 | 228 |
| 325 void ceee_module_util::Lock() { | 229 void ceee_module_util::Lock() { |
| 326 module.m_csStaticDataInitAndTypeInfo.Lock(); | 230 module.m_csStaticDataInitAndTypeInfo.Lock(); |
| 327 } | 231 } |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 378 } | 282 } |
| 379 | 283 |
| 380 CEEE_DEFINE_DLL_REGISTER_SERVER() | 284 CEEE_DEFINE_DLL_REGISTER_SERVER() |
| 381 | 285 |
| 382 // DllUnregisterServer - Removes entries from the system registry | 286 // DllUnregisterServer - Removes entries from the system registry |
| 383 STDAPI DllUnregisterServer(void) { | 287 STDAPI DllUnregisterServer(void) { |
| 384 // We always allow unregistration, even if no --enable-ceee install flag. | 288 // We always allow unregistration, even if no --enable-ceee install flag. |
| 385 HRESULT hr = module.DllUnregisterServer(); | 289 HRESULT hr = module.DllUnregisterServer(); |
| 386 return hr; | 290 return hr; |
| 387 } | 291 } |
| OLD | NEW |