FireBreath  1.4.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Pages
ActiveXBrowserHost.cpp
1 /**********************************************************\
2 Original Author: Richard Bateman (taxilian)
3 
4 Created: Oct 30, 2009
5 License: Dual license model; choose one of two:
6  New BSD License
7  http://www.opensource.org/licenses/bsd-license.php
8  - or -
9  GNU Lesser General Public License, version 2.1
10  http://www.gnu.org/licenses/lgpl-2.1.html
11 
12 Copyright 2009 Richard Bateman, Firebreath development team
13 \**********************************************************/
14 
15 #include "win_targetver.h"
16 #include "win_common.h"
17 #include <boost/assign.hpp>
18 #include <boost/smart_ptr/make_shared.hpp>
19 #include "axstream.h"
20 #include "DOM/Document.h"
21 #include "DOM/Window.h"
22 #include "AsyncFunctionCall.h"
23 #include "Win/WinMessageWindow.h"
24 #include "AXDOM/Window.h"
25 #include "AXDOM/Document.h"
26 #include "AXDOM/Element.h"
27 #include "AXDOM/Node.h"
28 
29 #include "ComVariantUtil.h"
30 #include "ActiveXFactoryDefinitions.h"
31 #include "ActiveXBrowserHost.h"
32 #include "BrowserStreamRequest.h"
33 #include "precompiled_headers.h" // On windows, everything above this line in PCH
34 
35 using namespace FB;
36 using namespace FB::ActiveX;
37 
38 ActiveXBrowserHost::ActiveXBrowserHost(IWebBrowser2 *doc, IOleClientSite* site)
39  : m_messageWin(new FB::WinMessageWindow())
40 {
41  resume(doc, site);
42 }
43 
44 ActiveXBrowserHost::~ActiveXBrowserHost(void)
45 {
46  if (!isShutDown())
47  shutdown();
48 }
49 
50 bool ActiveXBrowserHost::_scheduleAsyncCall(void (*func)(void *), void *userData) const
51 {
52  boost::shared_lock<boost::shared_mutex> _l(m_xtmutex);
53  if (!isShutDown() && m_messageWin) {
54  FBLOG_TRACE("ActiveXHost", "Scheduling async call for main thread");
55  return ::PostMessage(m_messageWin->getHWND(), WM_ASYNCTHREADINVOKE, NULL,
56  (LPARAM)new FB::AsyncFunctionCall(func, userData)) ? true : false;
57  } else {
58  return false;
59  }
60 }
61 
63 {
64  return (void*)this;
65 }
66 
67 FB::DOM::WindowPtr ActiveXBrowserHost::_createWindow(const FB::JSObjectPtr& obj) const
68 {
69  return FB::DOM::WindowPtr(new AXDOM::Window(ptr_cast<IDispatchAPI>(obj), m_webBrowser));
70 }
71 
72 FB::DOM::DocumentPtr ActiveXBrowserHost::_createDocument(const FB::JSObjectPtr& obj) const
73 {
74  return FB::DOM::DocumentPtr(new AXDOM::Document(ptr_cast<IDispatchAPI>(obj), m_webBrowser));
75 }
76 
77 FB::DOM::ElementPtr ActiveXBrowserHost::_createElement(const FB::JSObjectPtr& obj) const
78 {
79  return FB::DOM::ElementPtr(new AXDOM::Element(ptr_cast<IDispatchAPI>(obj), m_webBrowser));
80 }
81 
82 FB::DOM::NodePtr ActiveXBrowserHost::_createNode(const FB::JSObjectPtr& obj) const
83 {
84  return FB::DOM::NodePtr(new AXDOM::Node(ptr_cast<IDispatchAPI>(obj), m_webBrowser));
85 }
86 
87 void ActiveXBrowserHost::initDOMObjects()
88 {
89  // m_htmlWin/m_htmlDocDisp will be null after suspend()
90  if (!m_window && m_htmlWin) {
91  m_window = DOM::Window::create(IDispatchAPI::create(m_htmlWin, ptr_cast<ActiveXBrowserHost>(shared_from_this())));
92  }
93  if (!m_document && m_htmlDocDisp) {
94  m_document = DOM::Document::create(IDispatchAPI::create(m_htmlDocDisp, ptr_cast<ActiveXBrowserHost>(shared_from_this())));
95  }
96 }
97 
99 {
100  initDOMObjects();
101  return m_document;
102 }
103 
105 {
106  initDOMObjects();
107  return m_window;
108 }
109 
111 {
112  CComQIPtr<IOleControlSite> site(m_spClientSite);
113  CComPtr<IDispatch> dispatch;
114  site->GetExtendedControl(&dispatch);
115  CComQIPtr<IHTMLElement2> htmlElement(dispatch);
116  return DOM::Document::create(IDispatchAPI::create(htmlElement, ptr_cast<ActiveXBrowserHost>(shared_from_this())));
117 }
118 
119 void ActiveXBrowserHost::evaluateJavaScript(const std::string &script)
120 {
121  if(!m_htmlWin) {
122  throw FB::script_error("Can't execute JavaScript: Window is NULL");
123  }
124 
125  CComVariant res;
126  HRESULT hr = m_htmlWin->execScript(CComBSTR(script.c_str()),
127  CComBSTR("javascript"), &res);
128  if (SUCCEEDED(hr)) {
129  /* Throw away returned VARIANT, this method always returns VT_EMPTY.
130  http://msdn.microsoft.com/en-us/library/aa741364(VS.85).aspx */
131  return;
132  } else {
133  throw FB::script_error("Error executing JavaScript code");
134  }
135 }
136 
138 {
139  {
140  // First, make sure that no async calls are made while we're shutting down
141  boost::upgrade_lock<boost::shared_mutex> _l(m_xtmutex);
142  // Next, kill the message window so that none that have been made go through
143  m_messageWin.reset();
144  }
145  ReleaseAllHeldObjects();
146 
147  // Finally, run the main browser shutdown, which will fire off any cross-thread
148  // calls that somehow haven't made it through yet
150 
151  // Once that's done let's release any ActiveX resources that the browserhost
152  // is holding
153  suspend();
154  assert(m_deferredObjects.empty());
155 }
156 
157 void ActiveXBrowserHost::suspend()
158 {
159  // release any ActiveX resources that the browserhost is holding
160  m_webBrowser.Release();
161  m_spClientSite.Release();
162  m_htmlDocDisp.Release();
163  m_htmlWin.Release();
164 
165  // These are created on demand, don't need to be restored
166  m_window.reset();
167  m_document.reset();
168 
170 }
171 void ActiveXBrowserHost::resume(IWebBrowser2 *doc, IOleClientSite* clientSite)
172 {
173  m_webBrowser = doc;
174  m_spClientSite = clientSite;
175  if (m_webBrowser && !m_htmlDocDisp) {
176  m_webBrowser->get_Document(&m_htmlDocDisp);
177  CComQIPtr<IHTMLDocument2> doc(m_htmlDocDisp);
178  assert(doc);
179  doc->get_parentWindow(&m_htmlWin);
180  assert(m_htmlWin);
181  }
182 }
183 
184 FB::variant ActiveXBrowserHost::getVariant(const VARIANT *cVar)
185 {
186  CComVariant converted;
187  FB::variant retVal;
188 
189  switch(cVar->vt)
190  {
191  case VT_R4:
192  case VT_R8:
193  case VT_DECIMAL:
194  converted.ChangeType(VT_R8, cVar);
195  retVal = (double)converted.dblVal;
196  break;
197 
198  case VT_I1:
199  case VT_I2:
200  case VT_I4:
201  case VT_UI1:
202  case VT_UI2:
203  case VT_INT:
204  converted.ChangeType(VT_I4, cVar);
205  retVal = (long)converted.lVal;
206  break;
207  case VT_UI4:
208  case VT_UINT:
209  converted.ChangeType(VT_UI4, cVar);
210  retVal = (unsigned long)converted.ulVal;
211  break;
212 
213  case VT_I8:
214  retVal = static_cast<boost::int64_t>(cVar->llVal);
215  break;
216  case VT_UI8:
217  retVal = static_cast<boost::uint64_t>(cVar->ullVal);
218  case VT_LPSTR:
219  case VT_LPWSTR:
220  case VT_BSTR:
221  case VT_CLSID:
222  {
223  converted.ChangeType(VT_BSTR, cVar);
224  std::wstring wStr(converted.bstrVal);
225 
226  // return it as a UTF8 std::string
227  retVal = FB::wstring_to_utf8(wStr);
228  }
229  break;
230 
231  case VT_DISPATCH:
232  retVal = FB::JSObjectPtr(IDispatchAPI::create(cVar->pdispVal, ptr_cast<ActiveXBrowserHost>(shared_from_this())));
233  break;
234 
235  case VT_ERROR:
236  case VT_BOOL:
237  converted.ChangeType(VT_BOOL, cVar);
238  retVal = (converted.boolVal == VARIANT_TRUE) ? true : false;
239  break;
240 
241  case VT_NULL:
242  retVal = FB::FBNull();
243  break;
244 
245  case VT_EMPTY:
246  default:
247  // retVal is already empty, leave it such
248  break;
249  }
250 
251  return retVal;
252 }
253 
254 void ActiveXBrowserHost::getComVariant(VARIANT *dest, const FB::variant &var)
255 {
256  CComVariant outVar;
257  ::VariantInit(dest);
258 
259  const ComVariantBuilderMap& builderMap = getComVariantBuilderMap();
260  const std::type_info& type = var.get_type();
261  ComVariantBuilderMap::const_iterator it = builderMap.find(&type);
262 
263  if (it == builderMap.end()) {
264  // unhandled type :(
265  return;
266  }
267 
268  outVar = (it->second)(FB::ptr_cast<ActiveXBrowserHost>(shared_from_this()), var);
269 
270  outVar.Detach(dest);
271 }
272 
273 FB::BrowserStreamPtr FB::ActiveX::ActiveXBrowserHost::_createStream( const BrowserStreamRequest& req ) const
274 {
275  assertMainThread();
276  std::string url(req.uri.toString());
277  ActiveXStreamPtr stream;
278  if (req.method == "POST") {
279  stream = boost::make_shared<ActiveXStream>(url, req.cache, req.seekable, req.internalBufferSize, req.getPostData());
280  } else {
281  stream = boost::make_shared<ActiveXStream>(url, req.cache, req.seekable, req.internalBufferSize);
282  }
283  if (req.getEventSink()) {
284  stream->AttachObserver( req.getEventSink() );
285  }
286 
287  if ( stream->init() )
288  {
289  StreamCreatedEvent ev(stream.get());
290  stream->SendEvent( &ev );
291  if ( req.seekable ) stream->signalOpened();
292  }
293  else
294  {
295  stream.reset();
296  }
297  return stream;
298 }
299 
300 bool isExpired(std::pair<void*, FB::WeakIDispatchExRef> cur) {
301  return cur.second.expired();
302 }
303 
305 {
307  IDispatchWRef deferred;
308  while (m_deferredObjects.try_pop(deferred)) {
309  if (deferred.expired())
310  continue;
311  IDispatchSRef ptr(deferred.lock());
312  IDispatchRefList::iterator it(m_heldIDispatch.begin());
313  IDispatchRefList::iterator end(m_heldIDispatch.end());
314  while (it != end) {
315  if (*it == ptr) {
316  m_heldIDispatch.erase(it);
317  break;
318  } else ++it;
319  }
320  ptr->getPtr()->Release();
321  }
322  // Also remove any expired cached IDispatch WeakReferences
323  IDispatchExRefMap::iterator iter = m_cachedIDispatch.begin();
324  IDispatchExRefMap::iterator endIter = m_cachedIDispatch.end();
325  while (iter != endIter) {
326  if (isExpired(*iter))
327  iter = m_cachedIDispatch.erase(iter);
328  else
329  ++iter;
330  }
331 }
332 
333 
334 void FB::ActiveX::ActiveXBrowserHost::deferred_release( const IDispatchWRef& obj ) const
335 {
336  m_deferredObjects.push(obj);
337  if (isMainThread()) {
338  DoDeferredRelease();
339  }
340 }
341 
342 IDispatchEx* FB::ActiveX::ActiveXBrowserHost::getJSAPIWrapper( const FB::JSAPIWeakPtr& api, bool autoRelease/* = false*/ )
343 {
344  assertMainThread(); // This should only be called on the main thread
345  typedef boost::shared_ptr<FB::ShareableReference<IDispatchEx> > SharedIDispatchRef;
346  IDispatchEx* ret(NULL);
347  FB::JSAPIPtr ptr(api.lock());
348  if (!ptr)
349  return getFactoryInstance()->createCOMJSObject(shared_from_this(), api, autoRelease);
350 
351  IDispatchExRefMap::iterator fnd = m_cachedIDispatch.find(ptr.get());
352  if (fnd != m_cachedIDispatch.end()) {
353  SharedIDispatchRef ref(fnd->second.lock());
354  if (ref) {
355  // Fortunately this doesn't have to be threadsafe since this method only gets called
356  // from the main thread and the browser access happens on that thread as well!
357  ret = ref->getPtr();
358  ret->AddRef();
359  } else {
360  m_cachedIDispatch.erase(fnd);
361  }
362  }
363  if (!ret) {
364  ret = getFactoryInstance()->createCOMJSObject(shared_from_this(), api, autoRelease);
365  m_cachedIDispatch[ptr.get()] = _getWeakRefFromCOMJSWrapper(ret);
366  }
367  return ret;
368 }
369 
370 FB::ActiveX::IDispatchWRef FB::ActiveX::ActiveXBrowserHost::getIDispatchRef( IDispatch* obj )
371 {
372  IDispatchSRef ref(boost::make_shared<FB::ShareableReference<IDispatch> >(obj));
373  obj->AddRef();
374  m_heldIDispatch.push_back(ref);
375  return ref;
376 }
377 
378 void FB::ActiveX::ActiveXBrowserHost::ReleaseAllHeldObjects()
379 {
380  for (IDispatchRefList::iterator it(m_heldIDispatch.begin()); it != m_heldIDispatch.end(); ++it) {
381  (*it)->getPtr()->Release();
382  }
383  m_heldIDispatch.clear();
384 }
385 
386 void FB::ActiveX::ActiveXBrowserHost::Navigate( const std::string& url, const std::string& target )
387 {
388  CComBSTR destURL(FB::utf8_to_wstring(url).c_str());
389  CComVariant targetWin(FB::utf8_to_wstring(target).c_str());
390 
391  CComVariant vEmpty;
392 
393  HRESULT hr = m_webBrowser->Navigate(destURL, &vEmpty, &targetWin, &vEmpty, &vEmpty);
394  assert(SUCCEEDED(hr));
395 }
Creates a message window. Don't touch this if you don't understand what you are doing.
ActiveX specific implementation of DOM::Document.
const std::type_info & get_type() const
Gets the type of the value stored in variant.
Definition: variant.h:307
bool try_pop(Data &popped_value)
Try to pop a value off the front of the queue; if the queue is empty returns false.
Definition: SafeQueue.h:75
boost::shared_ptr< FB::JSObject > JSObjectPtr
Defines an alias representing a JSObject shared_ptr (you should never use a JSObject* directly) ...
Definition: APITypes.h:109
ActiveX specific implementation of DOM::Window.
This event is fired when the given stream was created.
Definition: StreamEvents.h:48
boost::shared_ptr< Node > NodePtr
shared_ptr for a FB::DOM::Node
Accepts any datatype, used in all interactions with javascript. Provides tools for getting back out t...
Definition: variant.h:198
bool empty() const
Queries if the queue is empty.
Definition: SafeQueue.h:60
boost::shared_ptr< Window > WindowPtr
shared_ptr for a FB::DOM::Window
boost::shared_ptr< Document > DocumentPtr
shared_ptr for a FB::DOM::Document
void evaluateJavaScript(const std::string &script)
Evaluates arbitrary javascript; note that it does not return the result due to cross- browser compati...
boost::weak_ptr< FB::JSAPI > JSAPIWeakPtr
Defines an alias for a JSAPI weak_ptr (you should never use a JSAPI* directly)
Definition: APITypes.h:88
FB::DOM::ElementPtr getDOMElement()
Gets a DOM::Element wrapper for the DOM/JS object tag that the plugin resides in. ...
virtual void * getContextID() const
Gets a unique identifier for this BrowserHost. There are cases where you may need multiple BrowserHos...
void shutdown()
Notifies the browserhost object that the associated plugin object is shutting down.
Information about an HTTP request to be made.
void DoDeferredRelease() const
Releases any browser-specific objects that were destroyed on a thread other than the main thread...
bool isShutDown() const
returns true if the FB::BrowserHost::shutdown() method has been called on this object ...
Definition: BrowserHost.h:417
static DocumentPtr create(const FB::JSObjectPtr &api)
Creates a FB::DOM::Document object from a JSObjectPtr representing a DOM object. This will probably t...
boost::shared_ptr< FB::JSAPI > JSAPIPtr
Defines an alias for a JSAPI shared_ptr (you should never use a JSAPI* directly)
Definition: APITypes.h:94
Exception type; when thrown in a JSAPI method, a javascript exception will be thrown.
Definition: JSExceptions.h:28
std::string wstring_to_utf8(const std::wstring &src)
Accepts a std::wstring and returns a UTF8-encoded std::string.
Definition: utf8_tools.cpp:37
Provides an ActiveX specific implementation of DOM::Node.
static WindowPtr create(const FB::JSObjectPtr &api)
Creates a FB::DOM::Window object from a JSObjectPtr representing a DOM object. This will probably thr...
virtual void shutdown()
Notifies the browserhost object that the associated plugin object is shutting down.
Definition: BrowserHost.cpp:87
void assertMainThread() const
When running in debug mode, asserts that the call is made on the main thread.
PluginEventSinkPtr getEventSink() const
Returns the PluginEventSink assigned to be the observer for the BrowserStream, or a NULL BrowserStrea...
boost::shared_ptr< Element > ElementPtr
shared_ptr for a FB::DOM::Element
ActiveX specific implementation of DOM::Element.
std::string getPostData() const
Returns POST data for the object, if any.
FB::DOM::WindowPtr getDOMWindow()
Gets a DOM::Window wrapper for the DOM/JS window object that the plugin resides in.
void Navigate(const std::string &url, const std::string &target)
Instructs the browser to navigate to the specified url in the target window.
FB::DOM::DocumentPtr getDOMDocument()
Gets a DOM::Document wrapper for the document object that the plugin resides in.
std::wstring utf8_to_wstring(const std::string &src)
Accepts a UTF8-encoded std::string and returns a std::wstring.
Definition: utf8_tools.cpp:50