PageRenderTime 26ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/src/ActiveXCore/ActiveXBrowserHost.cpp

https://github.com/GordonSmith/FireBreath
C++ | 395 lines | 314 code | 51 blank | 30 comment | 41 complexity | c216e492315f9bc040e1d7c692e38548 MD5 | raw file
  1. /**********************************************************\
  2. Original Author: Richard Bateman (taxilian)
  3. Created: Oct 30, 2009
  4. License: Dual license model; choose one of two:
  5. New BSD License
  6. http://www.opensource.org/licenses/bsd-license.php
  7. - or -
  8. GNU Lesser General Public License, version 2.1
  9. http://www.gnu.org/licenses/lgpl-2.1.html
  10. Copyright 2009 Richard Bateman, Firebreath development team
  11. \**********************************************************/
  12. #include "win_targetver.h"
  13. #include "win_common.h"
  14. #include <boost/assign.hpp>
  15. #include <boost/smart_ptr/make_shared.hpp>
  16. #include "axstream.h"
  17. #include "DOM/Document.h"
  18. #include "DOM/Window.h"
  19. #include "AsyncFunctionCall.h"
  20. #include "Win/WinMessageWindow.h"
  21. #include "AXDOM/Window.h"
  22. #include "AXDOM/Document.h"
  23. #include "AXDOM/Element.h"
  24. #include "AXDOM/Node.h"
  25. #include "ComVariantUtil.h"
  26. #include "ActiveXFactoryDefinitions.h"
  27. #include "ActiveXBrowserHost.h"
  28. #include "BrowserStreamRequest.h"
  29. #include "precompiled_headers.h" // On windows, everything above this line in PCH
  30. using namespace FB;
  31. using namespace FB::ActiveX;
  32. ActiveXBrowserHost::ActiveXBrowserHost(IWebBrowser2 *doc, IOleClientSite* site)
  33. : m_messageWin(new FB::WinMessageWindow())
  34. {
  35. resume(doc, site);
  36. }
  37. ActiveXBrowserHost::~ActiveXBrowserHost(void)
  38. {
  39. if (!isShutDown())
  40. shutdown();
  41. }
  42. bool ActiveXBrowserHost::_scheduleAsyncCall(void (*func)(void *), void *userData) const
  43. {
  44. boost::shared_lock<boost::shared_mutex> _l(m_xtmutex);
  45. if (!isShutDown() && m_messageWin) {
  46. FBLOG_TRACE("ActiveXHost", "Scheduling async call for main thread");
  47. return ::PostMessage(m_messageWin->getHWND(), WM_ASYNCTHREADINVOKE, NULL,
  48. (LPARAM)new FB::AsyncFunctionCall(func, userData)) ? true : false;
  49. } else {
  50. return false;
  51. }
  52. }
  53. void *ActiveXBrowserHost::getContextID() const
  54. {
  55. return (void*)this;
  56. }
  57. FB::DOM::WindowPtr ActiveXBrowserHost::_createWindow(const FB::JSObjectPtr& obj) const
  58. {
  59. return FB::DOM::WindowPtr(new AXDOM::Window(ptr_cast<IDispatchAPI>(obj), m_webBrowser));
  60. }
  61. FB::DOM::DocumentPtr ActiveXBrowserHost::_createDocument(const FB::JSObjectPtr& obj) const
  62. {
  63. return FB::DOM::DocumentPtr(new AXDOM::Document(ptr_cast<IDispatchAPI>(obj), m_webBrowser));
  64. }
  65. FB::DOM::ElementPtr ActiveXBrowserHost::_createElement(const FB::JSObjectPtr& obj) const
  66. {
  67. return FB::DOM::ElementPtr(new AXDOM::Element(ptr_cast<IDispatchAPI>(obj), m_webBrowser));
  68. }
  69. FB::DOM::NodePtr ActiveXBrowserHost::_createNode(const FB::JSObjectPtr& obj) const
  70. {
  71. return FB::DOM::NodePtr(new AXDOM::Node(ptr_cast<IDispatchAPI>(obj), m_webBrowser));
  72. }
  73. void ActiveXBrowserHost::initDOMObjects()
  74. {
  75. // m_htmlWin/m_htmlDocDisp will be null after suspend()
  76. if (!m_window && m_htmlWin) {
  77. m_window = DOM::Window::create(IDispatchAPI::create(m_htmlWin, ptr_cast<ActiveXBrowserHost>(shared_from_this())));
  78. }
  79. if (!m_document && m_htmlDocDisp) {
  80. m_document = DOM::Document::create(IDispatchAPI::create(m_htmlDocDisp, ptr_cast<ActiveXBrowserHost>(shared_from_this())));
  81. }
  82. }
  83. FB::DOM::DocumentPtr ActiveXBrowserHost::getDOMDocument()
  84. {
  85. initDOMObjects();
  86. return m_document;
  87. }
  88. FB::DOM::WindowPtr ActiveXBrowserHost::getDOMWindow()
  89. {
  90. initDOMObjects();
  91. return m_window;
  92. }
  93. FB::DOM::ElementPtr ActiveXBrowserHost::getDOMElement()
  94. {
  95. CComQIPtr<IOleControlSite> site(m_spClientSite);
  96. CComPtr<IDispatch> dispatch;
  97. site->GetExtendedControl(&dispatch);
  98. CComQIPtr<IHTMLElement2> htmlElement(dispatch);
  99. return DOM::Document::create(IDispatchAPI::create(htmlElement, ptr_cast<ActiveXBrowserHost>(shared_from_this())));
  100. }
  101. void ActiveXBrowserHost::evaluateJavaScript(const std::string &script)
  102. {
  103. if(!m_htmlWin) {
  104. throw FB::script_error("Can't execute JavaScript: Window is NULL");
  105. }
  106. CComVariant res;
  107. HRESULT hr = m_htmlWin->execScript(CComBSTR(script.c_str()),
  108. CComBSTR("javascript"), &res);
  109. if (SUCCEEDED(hr)) {
  110. /* Throw away returned VARIANT, this method always returns VT_EMPTY.
  111. http://msdn.microsoft.com/en-us/library/aa741364(VS.85).aspx */
  112. return;
  113. } else {
  114. throw FB::script_error("Error executing JavaScript code");
  115. }
  116. }
  117. void ActiveXBrowserHost::shutdown()
  118. {
  119. {
  120. // First, make sure that no async calls are made while we're shutting down
  121. boost::upgrade_lock<boost::shared_mutex> _l(m_xtmutex);
  122. // Next, kill the message window so that none that have been made go through
  123. m_messageWin.reset();
  124. }
  125. ReleaseAllHeldObjects();
  126. // Finally, run the main browser shutdown, which will fire off any cross-thread
  127. // calls that somehow haven't made it through yet
  128. BrowserHost::shutdown();
  129. // Once that's done let's release any ActiveX resources that the browserhost
  130. // is holding
  131. suspend();
  132. assert(m_deferredObjects.empty());
  133. }
  134. void ActiveXBrowserHost::suspend()
  135. {
  136. // release any ActiveX resources that the browserhost is holding
  137. m_webBrowser.Release();
  138. m_spClientSite.Release();
  139. m_htmlDocDisp.Release();
  140. m_htmlWin.Release();
  141. // These are created on demand, don't need to be restored
  142. m_window.reset();
  143. m_document.reset();
  144. DoDeferredRelease();
  145. }
  146. void ActiveXBrowserHost::resume(IWebBrowser2 *doc, IOleClientSite* clientSite)
  147. {
  148. m_webBrowser = doc;
  149. m_spClientSite = clientSite;
  150. if (m_webBrowser && !m_htmlDocDisp) {
  151. m_webBrowser->get_Document(&m_htmlDocDisp);
  152. CComQIPtr<IHTMLDocument2> doc(m_htmlDocDisp);
  153. assert(doc);
  154. doc->get_parentWindow(&m_htmlWin);
  155. assert(m_htmlWin);
  156. }
  157. }
  158. FB::variant ActiveXBrowserHost::getVariant(const VARIANT *cVar)
  159. {
  160. CComVariant converted;
  161. FB::variant retVal;
  162. switch(cVar->vt)
  163. {
  164. case VT_R4:
  165. case VT_R8:
  166. case VT_DECIMAL:
  167. converted.ChangeType(VT_R8, cVar);
  168. retVal = (double)converted.dblVal;
  169. break;
  170. case VT_I1:
  171. case VT_I2:
  172. case VT_I4:
  173. case VT_UI1:
  174. case VT_UI2:
  175. case VT_INT:
  176. converted.ChangeType(VT_I4, cVar);
  177. retVal = (long)converted.lVal;
  178. break;
  179. case VT_UI4:
  180. case VT_UINT:
  181. converted.ChangeType(VT_UI4, cVar);
  182. retVal = (unsigned long)converted.ulVal;
  183. break;
  184. case VT_I8:
  185. retVal = static_cast<boost::int64_t>(cVar->llVal);
  186. break;
  187. case VT_UI8:
  188. retVal = static_cast<boost::uint64_t>(cVar->ullVal);
  189. case VT_LPSTR:
  190. case VT_LPWSTR:
  191. case VT_BSTR:
  192. case VT_CLSID:
  193. {
  194. converted.ChangeType(VT_BSTR, cVar);
  195. std::wstring wStr(converted.bstrVal);
  196. // return it as a UTF8 std::string
  197. retVal = FB::wstring_to_utf8(wStr);
  198. }
  199. break;
  200. case VT_DISPATCH:
  201. retVal = FB::JSObjectPtr(IDispatchAPI::create(cVar->pdispVal, ptr_cast<ActiveXBrowserHost>(shared_from_this())));
  202. break;
  203. case VT_ERROR:
  204. case VT_BOOL:
  205. converted.ChangeType(VT_BOOL, cVar);
  206. retVal = (converted.boolVal == VARIANT_TRUE) ? true : false;
  207. break;
  208. case VT_NULL:
  209. retVal = FB::FBNull();
  210. break;
  211. case VT_EMPTY:
  212. default:
  213. // retVal is already empty, leave it such
  214. break;
  215. }
  216. return retVal;
  217. }
  218. void ActiveXBrowserHost::getComVariant(VARIANT *dest, const FB::variant &var)
  219. {
  220. CComVariant outVar;
  221. ::VariantInit(dest);
  222. const ComVariantBuilderMap& builderMap = getComVariantBuilderMap();
  223. const std::type_info& type = var.get_type();
  224. ComVariantBuilderMap::const_iterator it = builderMap.find(&type);
  225. if (it == builderMap.end()) {
  226. // unhandled type :(
  227. return;
  228. }
  229. outVar = (it->second)(FB::ptr_cast<ActiveXBrowserHost>(shared_from_this()), var);
  230. outVar.Detach(dest);
  231. }
  232. FB::BrowserStreamPtr FB::ActiveX::ActiveXBrowserHost::_createStream( const BrowserStreamRequest& req ) const
  233. {
  234. assertMainThread();
  235. std::string url(req.uri.toString());
  236. ActiveXStreamPtr stream;
  237. if (req.method == "POST") {
  238. stream = boost::make_shared<ActiveXStream>(url, req.cache, req.seekable, req.internalBufferSize, req.getPostData());
  239. } else {
  240. stream = boost::make_shared<ActiveXStream>(url, req.cache, req.seekable, req.internalBufferSize);
  241. }
  242. if (req.getEventSink()) {
  243. stream->AttachObserver( req.getEventSink() );
  244. }
  245. if ( stream->init() )
  246. {
  247. StreamCreatedEvent ev(stream.get());
  248. stream->SendEvent( &ev );
  249. if ( req.seekable ) stream->signalOpened();
  250. }
  251. else
  252. {
  253. stream.reset();
  254. }
  255. return stream;
  256. }
  257. bool isExpired(std::pair<void*, FB::WeakIDispatchExRef> cur) {
  258. return cur.second.expired();
  259. }
  260. void ActiveXBrowserHost::DoDeferredRelease() const
  261. {
  262. assertMainThread();
  263. IDispatchWRef deferred;
  264. while (m_deferredObjects.try_pop(deferred)) {
  265. if (deferred.expired())
  266. continue;
  267. IDispatchSRef ptr(deferred.lock());
  268. IDispatchRefList::iterator it(m_heldIDispatch.begin());
  269. IDispatchRefList::iterator end(m_heldIDispatch.end());
  270. while (it != end) {
  271. if (*it == ptr) {
  272. m_heldIDispatch.erase(it);
  273. break;
  274. } else ++it;
  275. }
  276. ptr->getPtr()->Release();
  277. }
  278. // Also remove any expired cached IDispatch WeakReferences
  279. IDispatchExRefMap::iterator iter = m_cachedIDispatch.begin();
  280. IDispatchExRefMap::iterator endIter = m_cachedIDispatch.end();
  281. while (iter != endIter) {
  282. if (isExpired(*iter))
  283. iter = m_cachedIDispatch.erase(iter);
  284. else
  285. ++iter;
  286. }
  287. }
  288. void FB::ActiveX::ActiveXBrowserHost::deferred_release( const IDispatchWRef& obj ) const
  289. {
  290. m_deferredObjects.push(obj);
  291. if (isMainThread()) {
  292. DoDeferredRelease();
  293. }
  294. }
  295. IDispatchEx* FB::ActiveX::ActiveXBrowserHost::getJSAPIWrapper( const FB::JSAPIWeakPtr& api, bool autoRelease/* = false*/ )
  296. {
  297. assertMainThread(); // This should only be called on the main thread
  298. typedef boost::shared_ptr<FB::ShareableReference<IDispatchEx> > SharedIDispatchRef;
  299. IDispatchEx* ret(NULL);
  300. FB::JSAPIPtr ptr(api.lock());
  301. if (!ptr)
  302. return getFactoryInstance()->createCOMJSObject(shared_from_this(), api, autoRelease);
  303. IDispatchExRefMap::iterator fnd = m_cachedIDispatch.find(ptr.get());
  304. if (fnd != m_cachedIDispatch.end()) {
  305. SharedIDispatchRef ref(fnd->second.lock());
  306. if (ref) {
  307. // Fortunately this doesn't have to be threadsafe since this method only gets called
  308. // from the main thread and the browser access happens on that thread as well!
  309. ret = ref->getPtr();
  310. ret->AddRef();
  311. } else {
  312. m_cachedIDispatch.erase(fnd);
  313. }
  314. }
  315. if (!ret) {
  316. ret = getFactoryInstance()->createCOMJSObject(shared_from_this(), api, autoRelease);
  317. m_cachedIDispatch[ptr.get()] = _getWeakRefFromCOMJSWrapper(ret);
  318. }
  319. return ret;
  320. }
  321. FB::ActiveX::IDispatchWRef FB::ActiveX::ActiveXBrowserHost::getIDispatchRef( IDispatch* obj )
  322. {
  323. IDispatchSRef ref(boost::make_shared<FB::ShareableReference<IDispatch> >(obj));
  324. obj->AddRef();
  325. m_heldIDispatch.push_back(ref);
  326. return ref;
  327. }
  328. void FB::ActiveX::ActiveXBrowserHost::ReleaseAllHeldObjects()
  329. {
  330. for (IDispatchRefList::iterator it(m_heldIDispatch.begin()); it != m_heldIDispatch.end(); ++it) {
  331. (*it)->getPtr()->Release();
  332. }
  333. m_heldIDispatch.clear();
  334. }
  335. void FB::ActiveX::ActiveXBrowserHost::Navigate( const std::string& url, const std::string& target )
  336. {
  337. CComBSTR destURL(FB::utf8_to_wstring(url).c_str());
  338. CComVariant targetWin(FB::utf8_to_wstring(target).c_str());
  339. CComVariant vEmpty;
  340. HRESULT hr = m_webBrowser->Navigate(destURL, &vEmpty, &targetWin, &vEmpty, &vEmpty);
  341. assert(SUCCEEDED(hr));
  342. }