/** * Event: OnAttachForgeExtensions */ void __stdcall CBrowserHelperObject::OnAttachForgeExtensions(IDispatch *dispatch, const wstring& location, const wstring& eventsource) { m_isAttached = false; auto manifest = _AtlModule.moduleManifest; CComPtr<IDispatch> disp = nullptr; CComQIPtr<IHTMLWindow2, &IID_IHTMLWindow2> htmlWindow2 = nullptr; HRESULT hr = S_OK; for (;;) { CComQIPtr<IWebBrowser2, &IID_IWebBrowser2> webBrowser2(dispatch); BreakOnNullWithErrorLog(webBrowser2, L"BrowserHelperObject::OnAttachForgeExtensions failed to obtain IWebBrowser2"); logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions -> " + boost::lexical_cast<wstring>(dispatch) + L" -> " + location + L" -> " + eventsource); // get interfaces hr = webBrowser2->get_Document(&disp); BreakOnNullWithErrorLog(disp, L"BrowserHelperObject::OnAttachForgeExtensions get_Document failed"); BreakOnFailed(hr); logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions IDispatch -> " + boost::lexical_cast<wstring>(disp)); CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> htmlDocument2(disp); BreakOnNullWithErrorLog(htmlDocument2, L"BrowserHelperObject::OnAttachForgeExtensions IHTMLDocument2 failed"); logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions IHTMLDocument2 -> " + boost::lexical_cast<wstring>(htmlDocument2)); hr = htmlDocument2->get_parentWindow(&htmlWindow2); BreakOnNullWithErrorLog(htmlWindow2, L"BrowserHelperObject::OnAttachForgeExtensions IHTMLWindow2 failed"); BreakOnFailed(hr); logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions IHTMLWindow2 -> " + boost::lexical_cast<wstring>(htmlWindow2)); CComQIPtr<IDispatchEx> htmlWindow2Ex(htmlWindow2); BreakOnNullWithErrorLog(htmlWindow2Ex, L"BrowserHelperObject::OnAttachForgeExtensions IHTMLWindow2Ex failed"); // Attach NativeAccessible (forge.tabs.*) if (m_nativeAccessible) { logger->error(L"BrowserHelperObject::OnAttachForgeExtensions resetting nativeAccessible"); m_nativeAccessible.reset(); } m_nativeAccessible = NativeAccessible::pointer(new NativeAccessible(webBrowser2)); hr = Attach::NativeTabs(htmlWindow2Ex, L"accessible", m_nativeAccessible.get()); BreakOnFailedWithErrorLog(hr, L"BrowserHelperObject::OnAttachForgeExtensions failed to attach NativeExtensions -> " + logger->parse(hr)); // Attach NativeExtensions hr = Attach::NativeExtensions(manifest->uuid, htmlWindow2Ex, L"extensions", m_instanceId, location, &m_nativeExtensions.p); BreakOnFailedWithErrorLog(hr, L"BrowserHelperObject::OnAttachForgeExtensions failed to attach NativeExtensions -> " + logger->parse(hr)); logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions attached NativeExtensions"); // Attach NativeMessaging hr = Attach::NativeMessaging(manifest->uuid, htmlWindow2Ex, L"messaging", m_instanceId, &m_nativeMessaging.p); BreakOnFailedWithErrorLog(hr, L"BrowserHelperObject::OnAttachForgeExtensions failed to attach NativeMessaging -> " + logger->parse(hr)); /// finally logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions attached NativeMessaging"); m_isAttached = true; break; } }
/** * Event: OnNavigateComplete2 */ void __stdcall CBrowserHelperObject::OnNavigateComplete2(IDispatch *dispatch, VARIANT *url) { CComBSTR bstr = nullptr; wstring location; HRESULT hr = S_OK; for (;;) { CComQIPtr<IWebBrowser2, &IID_IWebBrowser2> webBrowser2(dispatch); BreakOnNullWithErrorLog(webBrowser2, L"BrowserHelperObject::OnNavigateComplete2 failed to obtain IWebBrowser2"); // VARIANT *url chops off file:\\\ for local filesystem hr = webBrowser2->get_LocationURL(&bstr); BreakOnFailed(hr); location = wstring(bstr); if (location.empty() && url->bstrVal) // get_LocationURL fails in the most egregious of ways location = url->bstrVal; // match location against manifest auto manifest = _AtlModule.moduleManifest; auto& match = MatchManifest(webBrowser2, manifest, location); if (match.first.empty() && match.second.empty()) { logger->debug(L"BrowserHelperObject::OnNavigateComplete2 not interested -> " + manifest->uuid + L" -> " + wstring(url->bstrVal) + L" -> " + location); break; } logger->debug(L"BrowserHelperObject::OnNavigateComplete2 -> " + manifest->uuid + L" -> " + location); break; } // attach forge extensions OnAttachForgeExtensions(dispatch, location, L"OnNavigateComplete2"); }
/** * Event: OnNavigateComplete2 */ void __stdcall CBrowserHelperObject::OnNavigateComplete2(IDispatch *dispatch, VARIANT *url) { CComQIPtr<IWebBrowser2, &IID_IWebBrowser2> webBrowser2(dispatch); if (!webBrowser2) { logger->debug(L"BrowserHelperObject::OnNavigateComplete2 " L"failed to obtain IWebBrowser2"); return; } // VARIANT *url chops off file:\\\ for local filesystem CComBSTR bstr; webBrowser2->get_LocationURL(&bstr); wstring location(bstr); if (location == L"" && url->bstrVal) { // get_LocationURL fails in the most egregious of ways location = url->bstrVal; } // match location against manifest Manifest::pointer manifest = _AtlModule.moduleManifest; std::pair<wstringvector, wstringvector> match = this->MatchManifest(webBrowser2, manifest, location); if (match.first.size() == 0 && match.second.size() == 0) { logger->debug(L"BrowserHelperObject::OnNavigateComplete2 not interested" L" -> " + manifest->uuid + L" -> " + wstring(url->bstrVal) + L" -> " + location); return; } logger->debug(L"BrowserHelperObject::OnNavigateComplete2" L" -> " + manifest->uuid + L" -> " + location); // attach forge extensions this->OnAttachForgeExtensions(dispatch, location, L"OnNavigateComplete2"); }
/** * Event: OnAttachForgeExtensions */ void __stdcall CBrowserHelperObject::OnAttachForgeExtensions(IDispatch *dispatch, const wstring& location, const wstring& eventsource) { m_isAttached = false; CComQIPtr<IWebBrowser2, &IID_IWebBrowser2> webBrowser2(dispatch); if (!webBrowser2) { logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions " L"failed to obtain IWebBrowser2"); return; } logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions" L" -> " + boost::lexical_cast<wstring>(dispatch) + L" -> " + location + L" -> " + eventsource); Manifest::pointer manifest = _AtlModule.moduleManifest; // get interfaces CComPtr<IDispatch> disp; webBrowser2->get_Document(&disp); if (!disp) { logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions get_Document failed"); return; } logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions " L" IDispatch -> " + boost::lexical_cast<wstring>(disp)); CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> htmlDocument2(disp); if (!htmlDocument2) { logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions IHTMLDocument2 failed"); return; } logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions " L" IHTMLDocument2 -> " + boost::lexical_cast<wstring>(htmlDocument2)); CComQIPtr<IHTMLWindow2, &IID_IHTMLWindow2> htmlWindow2; htmlDocument2->get_parentWindow(&htmlWindow2); if (!htmlWindow2) { logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions IHTMLWindow2 failed"); return; } logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions " L" IHTMLWindow2 -> " + boost::lexical_cast<wstring>(htmlWindow2)); CComQIPtr<IDispatchEx> htmlWindow2Ex(htmlWindow2); if (!htmlWindow2Ex) { logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions IHTMLWindow2Ex failed"); return; } HRESULT hr; // Attach NativeAccessible (forge.tabs.*) if (m_nativeAccessible) { logger->error(L"BrowserHelperObject::OnAttachForgeExtensions resetting nativeAccessible"); m_nativeAccessible.reset(); } m_nativeAccessible = NativeAccessible::pointer(new NativeAccessible(webBrowser2)); hr = Attach::NativeTabs(htmlWindow2Ex, L"accessible", m_nativeAccessible.get()); if (FAILED(hr)) { logger->error(L"BrowserHelperObject::OnAttachForgeExtensions " L"failed to attach NativeExtensions" L" -> " + logger->parse(hr)); return; } // Attach NativeExtensions hr = Attach::NativeExtensions(manifest->uuid, htmlWindow2Ex, L"extensions", m_instanceId, location, &m_nativeExtensions.p); // "T** operator&() throw()" asserts on p==NULL if (FAILED(hr)) { logger->error(L"BrowserHelperObject::OnAttachForgeExtensions " L"failed to attach NativeExtensions" L" -> " + logger->parse(hr)); return; } logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions " L"attached NativeExtensions"); // Attach NativeMessaging hr = Attach::NativeMessaging(manifest->uuid, htmlWindow2Ex, L"messaging", m_instanceId, &m_nativeMessaging.p); // "T** operator&() throw()" asserts on p==NULL if (FAILED(hr)) { logger->error(L"BrowserHelperObject::OnAttachForgeExtensions " L"failed to attach NativeMessaging" L" -> " + logger->parse(hr)); return; } logger->debug(L"BrowserHelperObject::OnAttachForgeExtensions " L"attached NativeMessaging"); m_isAttached = true; }
/** * Event: OnDocumentComplete */ void __stdcall CBrowserHelperObject::OnDocumentComplete(IDispatch *dispatch, VARIANT *url) { Manifest::pointer manifest = _AtlModule.moduleManifest; CComQIPtr<IWebBrowser2, &IID_IWebBrowser2> webBrowser2(dispatch); if (!webBrowser2) { logger->debug(L"BrowserHelperObject::OnDocumentComplete " L"failed to obtain IWebBrowser2"); return; } // VARIANT *url chops off file:\\\ for local filesystem CComBSTR bstr; webBrowser2->get_LocationURL(&bstr); wstring location(bstr); if (location == L"" && url->bstrVal) { // get_LocationURL fails in the most egregious of ways location = url->bstrVal; } if (location == L"" && url->bstrVal == NULL) { logger->debug(L"BrowserHelperObject::OnDocumentComplete " L"blank location, not interested" L" -> " + manifest->uuid + L" -> " + location); return; } // match location against manifest std::pair<wstringvector, wstringvector> match = this->MatchManifest(webBrowser2, manifest, location); if (match.first.size() == 0 && match.second.size() == 0) { logger->debug(L"BrowserHelperObject::OnDocumentComplete not interested" L" -> " + manifest->uuid + L" -> " + wstring(url->bstrVal) + L" -> " + location); return; } logger->debug(L"BrowserHelperObject::OnDocumentComplete" L" -> " + manifest->uuid + L" -> " + wstring(url->bstrVal) + L" -> " + location); // get IHTMLWindow2 CComPtr<IDispatch> disp; webBrowser2->get_Document(&disp); if (!disp) { logger->debug(L"BrowserHelperObject::OnDocumentComplete get_Document failed"); return; } CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> htmlDocument2(disp); if (!htmlDocument2) { logger->debug(L"BrowserHelperObject::OnDocumentComplete IHTMLDocument2 failed"); return; } CComQIPtr<IHTMLWindow2, &IID_IHTMLWindow2> htmlWindow2; htmlDocument2->get_parentWindow(&htmlWindow2); if (!htmlWindow2) { logger->debug(L"BrowserHelperObject::OnDocumentComplete IHTMLWindow2 failed"); return; } // attach forge extensions when pages like target=_blank didn't trigger navComplete event if (!m_isAttached) { this->OnAttachForgeExtensions(dispatch, location, L"OnDocumentComplete"); } // Inject styles wstringset dupes; HTMLDocument document(webBrowser2); ScriptExtensions::scriptvector matches = m_scriptExtensions->read_styles(match.first); ScriptExtensions::scriptvector::const_iterator i = matches.begin(); for (; i != matches.end(); i++) { if (dupes.find(i->first) != dupes.end()) { logger->debug(L"BrowserHelperObject::OnDocumentComplete already injected -> " + i->first); continue; } wstringpointer style = i->second; if (!style) { logger->debug(L"BrowserHelperObject::OnDocumentComplete invalid stylesheet -> " + i->first); continue; } HRESULT hr; hr = document.InjectStyle(style); if (FAILED(hr)) { logger->error(L"BrowserHelperObject::OnDocumentComplete failed to inject style" L" -> " + i->first + L" -> " + logger->parse(hr)); continue; } dupes.insert(i->first); logger->debug(L"BrowserHelperObject::OnDocumentComplete injected: " + i->first); } // Inject scripts dupes.clear(); matches = m_scriptExtensions->read_scripts(match.second); i = matches.begin(); for (; i != matches.end(); i++) { if (dupes.find(i->first) != dupes.end()) { logger->debug(L"BrowserHelperObject::OnDocumentComplete already injected -> " + i->first); continue; } wstringpointer script = i->second; if (!script) { logger->debug(L"BrowserHelperObject::OnDocumentComplete invalid script -> " + i->first); continue; } HRESULT hr; //hr = document.InjectScript(script); //hr = document.InjectScriptTag(HTMLDocument::attrScriptType, i->first); CComVariant ret; hr = htmlWindow2->execScript(CComBSTR(script->c_str()), L"javascript", &ret); if (FAILED(hr)) { logger->error(L"BrowserHelperObject::OnDocumentComplete failed to inject script" L" -> " + i->first + L" -> " + logger->parse(hr)); continue; } dupes.insert(i->first); logger->debug(L"BrowserHelperObject::OnDocumentComplete injected" L" -> " + location + L" -> " + i->first); } /*// Test in-process IAccessible access SHANDLE_PTR phwnd; hr = webBrowser2->get_HWND(&phwnd); HWND hwnd = reinterpret_cast<HWND>(phwnd); AccessibleBrowser ab(hwnd); wstringvector tabs = ab.tabs(); for (wstringvector::const_iterator tab = tabs.begin(); tab != tabs.end(); tab++) { logger->debug(L"BrowserHelperObject::OnDocumentComplete -> " + *tab); }*/ }
// Implement refresh(F5 click, etc) based on // http://www.codeproject.com/Articles/3632/Detecting-the-IE-Refresh-button-using-IWebBrowser2 void CBrowserHelperObject::OnRefresh() { auto manifest = _AtlModule.moduleManifest; wstring location; wstringset dupes; CComBSTR bstr = nullptr; CComPtr<IDispatch> disp = nullptr; CComQIPtr<IHTMLWindow2, &IID_IHTMLWindow2> htmlWindow2 = nullptr; HRESULT hr = S_OK; for (;;) { // VARIANT *url chops off file:\\\ for local filesystem CComQIPtr<IWebBrowser2, &IID_IWebBrowser2> webBrowser2(m_webBrowser2); BreakOnNull(webBrowser2, hr); hr = webBrowser2->get_LocationURL(&bstr); BreakOnFailed(hr); location = wstring(bstr); // was m_strUrl if (location.empty()) { logger->debug(L"BrowserHelperObject::OnRefresh blank location, not interested -> " + manifest->uuid + L" -> " + location); break; } // match location against manifest auto& match = MatchManifest(webBrowser2, manifest, location); if (match.first.empty() && match.second.empty()) { logger->debug(L"BrowserHelperObject::OnRefresh not interested -> " + manifest->uuid + L" -> " + location); break; } logger->debug(L"BrowserHelperObject::OnRefresh -> " + manifest->uuid + L" -> " + location); // get IHTMLWindow2 hr = webBrowser2->get_Document(&disp); BreakOnNullWithErrorLog(disp, L"BrowserHelperObject::OnRefresh get_Document failed"); BreakOnFailed(hr); CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> htmlDocument2(disp); BreakOnNullWithErrorLog(htmlDocument2, L"BrowserHelperObject::OnRefresh IHTMLDocument2 failed"); hr = htmlDocument2->get_parentWindow(&htmlWindow2); BreakOnNullWithErrorLog(htmlWindow2, L"BrowserHelperObject::OnRefresh IHTMLWindow2 failed"); BreakOnFailed(hr); // attach forge extensions when pages like target=_blank didn't trigger navComplete event if (!m_isAttached) OnAttachForgeExtensions(m_webBrowser2, location, L"OnRefresh"); // was L"OnDocumentComplete" // Inject styles HTMLDocument document(webBrowser2); auto& matches = m_scriptExtensions->read_styles(match.first); for (auto& i : matches) { if (dupes.find(i.first) != dupes.end()) { logger->debug(L"BrowserHelperObject::OnRefresh already injected -> " + i.first); continue; } auto style = i.second; if (!style) { logger->debug(L"BrowserHelperObject::OnRefresh invalid stylesheet -> " + i.first); continue; } hr = document.InjectStyle(style); if (FAILED(hr)) { logger->error(L"BrowserHelperObject::OnRefresh failed to inject style -> " + i.first + L" -> " + logger->parse(hr)); continue; } dupes.insert(i.first); logger->debug(L"BrowserHelperObject::OnRefresh injected: " + i.first); } // Inject scripts dupes.clear(); matches = m_scriptExtensions->read_scripts(match.second); for (auto& i : matches) { if (dupes.find(i.first) != dupes.end()) { logger->debug(L"BrowserHelperObject::OnRefresh already injected -> " + i.first); continue; } auto script = i.second; if (!script) { logger->debug(L"BrowserHelperObject::OnRefresh invalid script -> " + i.first); continue; } CComVariant ret; hr = htmlWindow2->execScript(CComBSTR(script->c_str()), L"javascript", &ret); if (FAILED(hr)) { logger->error(L"BrowserHelperObject::OnRefresh failed to inject script -> " + i.first + L" -> " + logger->parse(hr)); continue; } dupes.insert(i.first); logger->debug(L"BrowserHelperObject::OnRefresh injected -> " + location + L" -> " + i.first); } break; } }