 Issue 5447868882092032:
  Issue 1793 - check whether the frame is whitelisted before injecting CSS  (Closed)
    
  
    Issue 5447868882092032:
  Issue 1793 - check whether the frame is whitelisted before injecting CSS  (Closed) 
  | Index: src/plugin/PluginTabBase.cpp | 
| diff --git a/src/plugin/PluginTabBase.cpp b/src/plugin/PluginTabBase.cpp | 
| index 3ac26f23bfca300ac710654ca2b406b6bd1be5c1..4acc8242924fc788481bdf495a08dc707300fdfc 100644 | 
| --- a/src/plugin/PluginTabBase.cpp | 
| +++ b/src/plugin/PluginTabBase.cpp | 
| @@ -1,394 +1,441 @@ | 
| -/* | 
| - * This file is part of Adblock Plus <https://adblockplus.org/>, | 
| - * Copyright (C) 2006-2015 Eyeo GmbH | 
| - * | 
| - * Adblock Plus is free software: you can redistribute it and/or modify | 
| - * it under the terms of the GNU General Public License version 3 as | 
| - * published by the Free Software Foundation. | 
| - * | 
| - * Adblock Plus is distributed in the hope that it will be useful, | 
| - * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
| - * GNU General Public License for more details. | 
| - * | 
| - * You should have received a copy of the GNU General Public License | 
| - * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 
| - */ | 
| - | 
| -#include "PluginStdAfx.h" | 
| -#include "AdblockPlusClient.h" | 
| -#include "PluginClientBase.h" | 
| -#include "PluginSettings.h" | 
| -#include "AdblockPlusDomTraverser.h" | 
| -#include "PluginTabBase.h" | 
| -#include "IeVersion.h" | 
| -#include <Mshtmhst.h> | 
| - | 
| -CPluginTabBase::CPluginTabBase(CPluginClass* plugin) | 
| - : m_plugin(plugin) | 
| - , m_isActivated(false) | 
| - , m_continueThreadRunning(true) | 
| -{ | 
| - m_filter = std::auto_ptr<CPluginFilter>(new CPluginFilter()); | 
| - m_filter->hideFiltersLoadedEvent = CreateEvent(NULL, true, false, NULL); | 
| - | 
| - CPluginClient* client = CPluginClient::GetInstance(); | 
| - if (AdblockPlus::IE::InstalledMajorVersion() < 10) | 
| - { | 
| - m_isActivated = true; | 
| - } | 
| - | 
| - try | 
| - { | 
| - m_thread = std::thread(&CPluginTabBase::ThreadProc, this); | 
| - } | 
| - catch (const std::system_error& ex) | 
| - { | 
| - DEBUG_SYSTEM_EXCEPTION(ex, PLUGIN_ERROR_THREAD, PLUGIN_ERROR_TAB_THREAD_CREATE_PROCESS, | 
| - "Tab::Thread - Failed to create tab thread"); | 
| - } | 
| - m_traverser = new CPluginDomTraverser(static_cast<CPluginTab*>(this)); | 
| -} | 
| - | 
| - | 
| -CPluginTabBase::~CPluginTabBase() | 
| -{ | 
| - delete m_traverser; | 
| - m_traverser = NULL; | 
| - m_continueThreadRunning = false; | 
| - if (m_thread.joinable()) { | 
| - m_thread.join(); | 
| - } | 
| -} | 
| - | 
| -void CPluginTabBase::OnActivate() | 
| -{ | 
| - m_isActivated = true; | 
| -} | 
| - | 
| - | 
| -void CPluginTabBase::OnUpdate() | 
| -{ | 
| - m_isActivated = true; | 
| -} | 
| - | 
| -namespace | 
| -{ | 
| - // Entry Point | 
| - void FilterLoader(CPluginTabBase* tabBase) | 
| - { | 
| - try | 
| - { | 
| - tabBase->m_filter->LoadHideFilters(CPluginClient::GetInstance()->GetElementHidingSelectors(tabBase->GetDocumentDomain())); | 
| - SetEvent(tabBase->m_filter->hideFiltersLoadedEvent); | 
| - } | 
| - catch (...) | 
| - { | 
| - // As a thread-main function, we truncate any C++ exception. | 
| - } | 
| - } | 
| -} | 
| - | 
| -void CPluginTabBase::OnNavigate(const std::wstring& url) | 
| -{ | 
| - SetDocumentUrl(url); | 
| - ClearFrameCache(GetDocumentDomain()); | 
| - std::wstring domainString = GetDocumentDomain(); | 
| - ResetEvent(m_filter->hideFiltersLoadedEvent); | 
| - try | 
| - { | 
| - std::thread filterLoaderThread(&FilterLoader, this); | 
| - filterLoaderThread.detach(); // TODO: but actually we should wait for the thread in the dtr. | 
| - } | 
| - catch (const std::system_error& ex) | 
| - { | 
| - DEBUG_SYSTEM_EXCEPTION(ex, PLUGIN_ERROR_THREAD, PLUGIN_ERROR_MAIN_THREAD_CREATE_PROCESS, | 
| - "Class::Thread - Failed to start filter loader thread"); | 
| - } | 
| - m_traverser->ClearCache(); | 
| -} | 
| - | 
| -namespace | 
| -{ | 
| - /** | 
| - * Determine if the HTML file is one of ours. | 
| - * The criterion is that it appear in the "html/templates" folder within our installation. | 
| - * | 
| - * Warning: This function may fail if the argument is not a "file://" URL. | 
| - * This is occasionally the case in circumstances yet to be characterized. | 
| - */ | 
| - bool IsOurHtmlFile(const std::wstring& url) | 
| - { | 
| - // Declared static because the value is derived from an installation directory, which won't change during run-time. | 
| - static auto dir = FileUrl(HtmlFolderPath()); | 
| - | 
| - DEBUG_GENERAL([&]() -> std::wstring { | 
| - std::wstring log = L"InjectABP. Current URL: "; | 
| - log += url; | 
| - log += L", template directory URL: "; | 
| - log += dir; | 
| - return log; | 
| - }()); | 
| - | 
| - /* | 
| - * The length check here is defensive, in case the document URL is truncated for some reason. | 
| - */ | 
| - if (url.length() < 5) | 
| - { | 
| - // We can't match ".html" at the end of the URL if it's too short. | 
| - return false; | 
| - } | 
| - auto urlCstr = url.c_str(); | 
| - // Check the prefix to match our directory | 
| - // Check the suffix to be an HTML file | 
| - return (_wcsnicmp(urlCstr, dir.c_str(), dir.length()) == 0) && | 
| - (_wcsnicmp(urlCstr + url.length() - 5, L".html", 5) == 0); | 
| - } | 
| -} | 
| - | 
| -void CPluginTabBase::InjectABP(IWebBrowser2* browser) | 
| -{ | 
| - CriticalSection::Lock lock(m_csInject); | 
| - auto url = GetDocumentUrl(); | 
| - if (!IsOurHtmlFile(url)) | 
| - { | 
| - DEBUG_GENERAL(L"InjectABP. Not injecting"); | 
| - return; | 
| - } | 
| - DEBUG_GENERAL(L"InjectABP. Injecting"); | 
| - CComPtr<IDispatch> pDocDispatch; | 
| - browser->get_Document(&pDocDispatch); | 
| - CComQIPtr<IHTMLDocument2> pDoc2 = pDocDispatch; | 
| - if (!pDoc2) | 
| - { | 
| - DEBUG_ERROR_LOG(0, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT_INVOKE, "CPluginTabBase::InjectABP - Failed to QI document"); | 
| - return; | 
| - } | 
| - CComPtr<IHTMLWindow2> pWnd2; | 
| - pDoc2->get_parentWindow(&pWnd2); | 
| - if (!pWnd2) | 
| - { | 
| - DEBUG_ERROR_LOG(0, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT_INVOKE, "CPluginTabBase::InjectABP - Failed to get parent window"); | 
| - return; | 
| - } | 
| - CComQIPtr<IDispatchEx> pWndEx = pWnd2; | 
| - if (!pWndEx) | 
| - { | 
| - DEBUG_ERROR_LOG(0, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT_INVOKE, "CPluginTabBase::InjectABP - Failed to QI dispatch"); | 
| - return; | 
| - } | 
| - // Create "Settings" object in JavaScript. | 
| - // A method call of "Settings" in JavaScript, transfered to "Invoke" of m_pluginUserSettings | 
| - DISPID dispid; | 
| - HRESULT hr = pWndEx->GetDispID(L"Settings", fdexNameEnsure, &dispid); | 
| - if (FAILED(hr)) | 
| - { | 
| - DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT_INVOKE, "CPluginTabBase::InjectABP - Failed to get dispatch"); | 
| - return; | 
| - } | 
| - CComVariant var((IDispatch*)&m_pluginUserSettings); | 
| - | 
| - DEBUG_GENERAL("Injecting"); | 
| - | 
| - DISPPARAMS params; | 
| - params.cArgs = 1; | 
| - params.cNamedArgs = 0; | 
| - params.rgvarg = &var; | 
| - params.rgdispidNamedArgs = 0; | 
| - hr = pWndEx->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF, ¶ms, 0, 0, 0); | 
| - DEBUG_GENERAL("Invoke"); | 
| - if (FAILED(hr)) | 
| - { | 
| - DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT_INVOKE, "CPluginTabBase::InjectABP - Failed to create Settings in JavaScript"); | 
| - } | 
| -} | 
| - | 
| -void CPluginTabBase::OnDownloadComplete(IWebBrowser2* browser) | 
| -{ | 
| - CPluginClient* client = CPluginClient::GetInstance(); | 
| - std::wstring url = GetDocumentUrl(); | 
| - if (!client->IsWhitelistedUrl(url) && !client->IsElemhideWhitelistedOnDomain(url)) | 
| - { | 
| - m_traverser->TraverseDocument(browser, GetDocumentDomain(), GetDocumentUrl()); | 
| - } | 
| - InjectABP(browser); | 
| -} | 
| - | 
| -void CPluginTabBase::OnDocumentComplete(IWebBrowser2* browser, const std::wstring& url, bool isDocumentBrowser) | 
| -{ | 
| - std::wstring documentUrl = GetDocumentUrl(); | 
| - | 
| - if (isDocumentBrowser) | 
| - { | 
| - if (url != documentUrl) | 
| - { | 
| - SetDocumentUrl(url); | 
| - } | 
| - InjectABP(browser); | 
| - } | 
| - CString urlLegacy = ToCString(url); | 
| - if (urlLegacy.Left(6) != "res://") | 
| - { | 
| - // Get document | 
| - CComPtr<IDispatch> pDocDispatch; | 
| - HRESULT hr = browser->get_Document(&pDocDispatch); | 
| - if (FAILED(hr) || !pDocDispatch) | 
| - { | 
| - return; | 
| - } | 
| - | 
| - CComQIPtr<IHTMLDocument2> pDoc = pDocDispatch; | 
| - if (!pDoc) | 
| - { | 
| - return; | 
| - } | 
| - CComPtr<IOleObject> pOleObj; | 
| - | 
| - pDocDispatch->QueryInterface(IID_IOleObject, (void**)&pOleObj); | 
| - | 
| - | 
| - CComPtr<IOleClientSite> pClientSite; | 
| - pOleObj->GetClientSite(&pClientSite); | 
| - if (pClientSite != NULL) | 
| - { | 
| - CComPtr<IDocHostUIHandler> docHostUIHandler; | 
| - pClientSite->QueryInterface(IID_IDocHostUIHandler, (void**)&docHostUIHandler); | 
| - if (docHostUIHandler != NULL) | 
| - { | 
| - docHostUIHandler->UpdateUI(); | 
| - } | 
| - } | 
| - } | 
| -} | 
| - | 
| -std::wstring CPluginTabBase::GetDocumentDomain() | 
| -{ | 
| - std::wstring domain; | 
| - | 
| - m_criticalSection.Lock(); | 
| - { | 
| - domain = m_documentDomain; | 
| - } | 
| - m_criticalSection.Unlock(); | 
| - | 
| - return domain; | 
| -} | 
| - | 
| -void CPluginTabBase::SetDocumentUrl(const std::wstring& url) | 
| -{ | 
| - m_criticalSection.Lock(); | 
| - { | 
| - m_documentUrl = url; | 
| - m_documentDomain = CAdblockPlusClient::GetInstance()->GetHostFromUrl(url); | 
| - } | 
| - m_criticalSection.Unlock(); | 
| -} | 
| - | 
| -std::wstring CPluginTabBase::GetDocumentUrl() | 
| -{ | 
| - std::wstring url; | 
| - | 
| - m_criticalSection.Lock(); | 
| - { | 
| - url = m_documentUrl; | 
| - } | 
| - m_criticalSection.Unlock(); | 
| - | 
| - return url; | 
| -} | 
| - | 
| - | 
| -// ============================================================================ | 
| -// Frame caching | 
| -// ============================================================================ | 
| -bool CPluginTabBase::IsFrameCached(const std::wstring& url) | 
| -{ | 
| - bool isFrame; | 
| - | 
| - m_criticalSectionCache.Lock(); | 
| - { | 
| - isFrame = m_cacheFrames.find(url) != m_cacheFrames.end(); | 
| - } | 
| - m_criticalSectionCache.Unlock(); | 
| - | 
| - return isFrame; | 
| -} | 
| - | 
| -void CPluginTabBase::CacheFrame(const std::wstring& url) | 
| -{ | 
| - m_criticalSectionCache.Lock(); | 
| - { | 
| - m_cacheFrames.insert(url); | 
| - } | 
| - m_criticalSectionCache.Unlock(); | 
| -} | 
| - | 
| -void CPluginTabBase::ClearFrameCache(const std::wstring& domain) | 
| -{ | 
| - m_criticalSectionCache.Lock(); | 
| - { | 
| - if (domain.empty() || domain != m_cacheDomain) | 
| - { | 
| - m_cacheFrames.clear(); | 
| - m_cacheDomain = domain; | 
| - } | 
| - } | 
| - m_criticalSectionCache.Unlock(); | 
| -} | 
| - | 
| -void CPluginTabBase::ThreadProc() | 
| -{ | 
| - // Force loading/creation of settings | 
| - CPluginSettings::GetInstance()->SetWorkingThreadId(); | 
| - | 
| - std::string message = | 
| - "================================================================================\n" | 
| - "TAB THREAD process="; | 
| - message += std::to_string(::GetCurrentProcessId()); | 
| - message + " thread="; | 
| - message += std::to_string(::GetCurrentThreadId()); | 
| - message += | 
| - "\n" | 
| - "================================================================================"; | 
| - DEBUG_GENERAL(message); | 
| - | 
| - // -------------------------------------------------------------------- | 
| - // Tab loop | 
| - // -------------------------------------------------------------------- | 
| - | 
| - DWORD loopCount = 0; | 
| - DWORD tabLoopIteration = 1; | 
| - | 
| - while (this->m_continueThreadRunning) | 
| - { | 
| -#ifdef ENABLE_DEBUG_THREAD | 
| - CStringA sTabLoopIteration; | 
| - sTabLoopIteration.Format("%u", tabLoopIteration); | 
| - | 
| - DEBUG_THREAD("--------------------------------------------------------------------------------") | 
| - DEBUG_THREAD("Loop iteration " + sTabLoopIteration); | 
| - DEBUG_THREAD("--------------------------------------------------------------------------------") | 
| -#endif | 
| - this->m_isActivated = false; | 
| - | 
| - // -------------------------------------------------------------------- | 
| - // End loop | 
| - // -------------------------------------------------------------------- | 
| - | 
| - // Sleep loop | 
| - while (this->m_continueThreadRunning && !this->m_isActivated && (++loopCount % (TIMER_THREAD_SLEEP_TAB_LOOP / 50)) != 0) | 
| - { | 
| - // Post async plugin error | 
| - CPluginError pluginError; | 
| - if (LogQueue::PopFirstPluginError(pluginError)) | 
| - { | 
| - LogQueue::LogPluginError(pluginError.GetErrorCode(), pluginError.GetErrorId(), pluginError.GetErrorSubid(), pluginError.GetErrorDescription(), true, pluginError.GetProcessId(), pluginError.GetThreadId()); | 
| - } | 
| - | 
| - // Non-hanging sleep | 
| - Sleep(50); | 
| - } | 
| - | 
| - tabLoopIteration++; | 
| - } | 
| -} | 
| +/* | 
| + * This file is part of Adblock Plus <https://adblockplus.org/>, | 
| + * Copyright (C) 2006-2015 Eyeo GmbH | 
| + * | 
| + * Adblock Plus is free software: you can redistribute it and/or modify | 
| + * it under the terms of the GNU General Public License version 3 as | 
| + * published by the Free Software Foundation. | 
| + * | 
| + * Adblock Plus is distributed in the hope that it will be useful, | 
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
| + * GNU General Public License for more details. | 
| + * | 
| + * You should have received a copy of the GNU General Public License | 
| + * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 
| + */ | 
| + | 
| +#include "PluginStdAfx.h" | 
| +#include "AdblockPlusClient.h" | 
| +#include "PluginClientBase.h" | 
| +#include "PluginSettings.h" | 
| +#include "AdblockPlusDomTraverser.h" | 
| +#include "PluginTabBase.h" | 
| +#include "IeVersion.h" | 
| +#include "../shared/Utils.h" | 
| +#include <Mshtmhst.h> | 
| + | 
| +CPluginTabBase::CPluginTabBase(CPluginClass* plugin) | 
| + : m_plugin(plugin) | 
| + , m_isActivated(false) | 
| + , m_continueThreadRunning(true) | 
| +{ | 
| + m_filter = std::auto_ptr<CPluginFilter>(new CPluginFilter()); | 
| + m_filter->hideFiltersLoadedEvent = CreateEvent(NULL, true, false, NULL); | 
| + | 
| + CPluginClient* client = CPluginClient::GetInstance(); | 
| + if (AdblockPlus::IE::InstalledMajorVersion() < 10) | 
| + { | 
| + m_isActivated = true; | 
| + } | 
| + | 
| + try | 
| + { | 
| + m_thread = std::thread(&CPluginTabBase::ThreadProc, this); | 
| + } | 
| + catch (const std::system_error& ex) | 
| + { | 
| + DEBUG_SYSTEM_EXCEPTION(ex, PLUGIN_ERROR_THREAD, PLUGIN_ERROR_TAB_THREAD_CREATE_PROCESS, | 
| + "Tab::Thread - Failed to create tab thread"); | 
| + } | 
| + m_traverser = new CPluginDomTraverser(static_cast<CPluginTab*>(this)); | 
| +} | 
| + | 
| + | 
| +CPluginTabBase::~CPluginTabBase() | 
| +{ | 
| + delete m_traverser; | 
| + m_traverser = NULL; | 
| + m_continueThreadRunning = false; | 
| + if (m_thread.joinable()) { | 
| + m_thread.join(); | 
| + } | 
| +} | 
| + | 
| +void CPluginTabBase::OnActivate() | 
| +{ | 
| + m_isActivated = true; | 
| +} | 
| + | 
| + | 
| +void CPluginTabBase::OnUpdate() | 
| +{ | 
| + m_isActivated = true; | 
| +} | 
| + | 
| +namespace | 
| +{ | 
| + // Entry Point | 
| + void FilterLoader(CPluginTabBase* tabBase) | 
| + { | 
| + try | 
| + { | 
| + tabBase->m_filter->LoadHideFilters(CPluginClient::GetInstance()->GetElementHidingSelectors(tabBase->GetDocumentDomain())); | 
| + SetEvent(tabBase->m_filter->hideFiltersLoadedEvent); | 
| + } | 
| + catch (...) | 
| + { | 
| + // As a thread-main function, we truncate any C++ exception. | 
| + } | 
| + } | 
| +} | 
| + | 
| +void CPluginTabBase::OnNavigate(const std::wstring& url) | 
| +{ | 
| + SetDocumentUrl(url); | 
| + ClearFrameCache(GetDocumentDomain()); | 
| + std::wstring domainString = GetDocumentDomain(); | 
| + ResetEvent(m_filter->hideFiltersLoadedEvent); | 
| + try | 
| + { | 
| + std::thread filterLoaderThread(&FilterLoader, this); | 
| + filterLoaderThread.detach(); // TODO: but actually we should wait for the thread in the dtr. | 
| + } | 
| + catch (const std::system_error& ex) | 
| + { | 
| + DEBUG_SYSTEM_EXCEPTION(ex, PLUGIN_ERROR_THREAD, PLUGIN_ERROR_MAIN_THREAD_CREATE_PROCESS, | 
| + "Class::Thread - Failed to start filter loader thread"); | 
| + } | 
| + m_traverser->ClearCache(); | 
| +} | 
| + | 
| +namespace | 
| +{ | 
| + /** | 
| + * Determine if the HTML file is one of ours. | 
| + * The criterion is that it appear in the "html/templates" folder within our installation. | 
| + * | 
| + * Warning: This function may fail if the argument is not a "file://" URL. | 
| + * This is occasionally the case in circumstances yet to be characterized. | 
| + */ | 
| + bool IsOurHtmlFile(const std::wstring& url) | 
| + { | 
| + // Declared static because the value is derived from an installation directory, which won't change during run-time. | 
| + static auto dir = FileUrl(HtmlFolderPath()); | 
| + | 
| + DEBUG_GENERAL([&]() -> std::wstring { | 
| + std::wstring log = L"InjectABP. Current URL: "; | 
| + log += url; | 
| + log += L", template directory URL: "; | 
| + log += dir; | 
| + return log; | 
| + }()); | 
| + | 
| + /* | 
| + * The length check here is defensive, in case the document URL is truncated for some reason. | 
| + */ | 
| + if (url.length() < 5) | 
| + { | 
| + // We can't match ".html" at the end of the URL if it's too short. | 
| + return false; | 
| + } | 
| + auto urlCstr = url.c_str(); | 
| + // Check the prefix to match our directory | 
| + // Check the suffix to be an HTML file | 
| + return (_wcsnicmp(urlCstr, dir.c_str(), dir.length()) == 0) && | 
| + (_wcsnicmp(urlCstr + url.length() - 5, L".html", 5) == 0); | 
| + } | 
| +} | 
| + | 
| +void CPluginTabBase::InjectABP(IWebBrowser2* browser) | 
| +{ | 
| + CriticalSection::Lock lock(m_csInject); | 
| + auto url = GetDocumentUrl(); | 
| + if (!IsOurHtmlFile(url)) | 
| + { | 
| + DEBUG_GENERAL(L"InjectABP. Not injecting"); | 
| + return; | 
| + } | 
| + DEBUG_GENERAL(L"InjectABP. Injecting"); | 
| + CComPtr<IDispatch> pDocDispatch; | 
| + browser->get_Document(&pDocDispatch); | 
| + CComQIPtr<IHTMLDocument2> pDoc2 = pDocDispatch; | 
| + if (!pDoc2) | 
| + { | 
| + DEBUG_ERROR_LOG(0, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT_INVOKE, "CPluginTabBase::InjectABP - Failed to QI document"); | 
| + return; | 
| + } | 
| + CComPtr<IHTMLWindow2> pWnd2; | 
| + pDoc2->get_parentWindow(&pWnd2); | 
| + if (!pWnd2) | 
| + { | 
| + DEBUG_ERROR_LOG(0, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT_INVOKE, "CPluginTabBase::InjectABP - Failed to get parent window"); | 
| + return; | 
| + } | 
| + CComQIPtr<IDispatchEx> pWndEx = pWnd2; | 
| + if (!pWndEx) | 
| + { | 
| + DEBUG_ERROR_LOG(0, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT_INVOKE, "CPluginTabBase::InjectABP - Failed to QI dispatch"); | 
| + return; | 
| + } | 
| + // Create "Settings" object in JavaScript. | 
| + // A method call of "Settings" in JavaScript, transfered to "Invoke" of m_pluginUserSettings | 
| + DISPID dispid; | 
| + HRESULT hr = pWndEx->GetDispID(L"Settings", fdexNameEnsure, &dispid); | 
| + if (FAILED(hr)) | 
| + { | 
| + DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT_INVOKE, "CPluginTabBase::InjectABP - Failed to get dispatch"); | 
| + return; | 
| + } | 
| + CComVariant var((IDispatch*)&m_pluginUserSettings); | 
| + | 
| + DEBUG_GENERAL("Injecting"); | 
| + | 
| + DISPPARAMS params; | 
| + params.cArgs = 1; | 
| + params.cNamedArgs = 0; | 
| + params.rgvarg = &var; | 
| + params.rgdispidNamedArgs = 0; | 
| + hr = pWndEx->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF, ¶ms, 0, 0, 0); | 
| + DEBUG_GENERAL("Invoke"); | 
| + if (FAILED(hr)) | 
| + { | 
| + DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT_INVOKE, "CPluginTabBase::InjectABP - Failed to create Settings in JavaScript"); | 
| + } | 
| +} | 
| + | 
| +namespace | 
| +{ | 
| + ATL::CComPtr<IWebBrowser2> GetParent(IWebBrowser2& browser) | 
| 
Eric
2015/11/17 22:30:22
IWebBrowser2* GetParent(IWebBrowser2& browser)
 | 
| + { | 
| + ATL::CComPtr<IDispatch> parentDispatch; | 
| + if (FAILED(browser.get_Parent(&parentDispatch)) || !parentDispatch) | 
| + { | 
| + return nullptr; | 
| + } | 
| + // The InternetExplorer application always returns a pointer to itself. | 
| + // https://msdn.microsoft.com/en-us/library/aa752136(v=vs.85).aspx | 
| + if (parentDispatch.IsEqualObject(&browser)) | 
| + { | 
| + return nullptr; | 
| + } | 
| 
Eric
2015/11/17 22:30:22
ATL::CComQIPtr<IWebBrowser2> parent = parentDispat
 
sergei
2015/11/18 10:07:50
Yes, this is just an assumption and it does not wo
 
Eric
2015/11/18 14:24:02
What do you mean "because `parent` is always nullp
 
sergei
2015/11/18 14:28:17
In your snippet
 | 
| + ATL::CComQIPtr<IServiceProvider> parentDocumentServiceProvider = parentDispatch; | 
| + if (!parentDocumentServiceProvider) | 
| + { | 
| + return nullptr; | 
| + } | 
| + ATL::CComPtr<IWebBrowserApp> webBrowserApp; | 
| + if (FAILED(parentDocumentServiceProvider->QueryService(IID_IWebBrowserApp, &webBrowserApp)) || !webBrowserApp) | 
| + { | 
| + return nullptr; | 
| + } | 
| + return ATL::CComQIPtr<IWebBrowser2>(webBrowserApp); | 
| + } | 
| 
Eric
2015/11/18 15:25:22
Except after the call to IsEqualObject(), many of
 | 
| + | 
| + bool IsFrameWhiteListed(ATL::CComPtr<IWebBrowser2> frame) | 
| + { | 
| + if (!frame) | 
| + { | 
| + return false; | 
| + } | 
| + auto url = GetLocationUrl(*frame); | 
| + std::vector<std::string> frameHierarchy; | 
| + while(frame = GetParent(*frame)) | 
| + { | 
| + frameHierarchy.push_back(ToUtf8String(GetLocationUrl(*frame))); | 
| + } | 
| + CPluginClient* client = CPluginClient::GetInstance(); | 
| + return client->IsWhitelistedUrl(url, frameHierarchy) | 
| + || client->IsElemhideWhitelistedOnDomain(url, frameHierarchy); | 
| + } | 
| +} | 
| + | 
| +void CPluginTabBase::OnDownloadComplete(IWebBrowser2* browser) | 
| +{ | 
| + CPluginClient* client = CPluginClient::GetInstance(); | 
| + std::wstring url = GetDocumentUrl(); | 
| + if (!client->IsWhitelistedUrl(url) && !client->IsElemhideWhitelistedOnDomain(url)) | 
| + { | 
| + m_traverser->TraverseDocument(browser, GetDocumentDomain(), GetDocumentUrl()); | 
| + } | 
| + InjectABP(browser); | 
| +} | 
| + | 
| +void CPluginTabBase::OnDocumentComplete(IWebBrowser2* browser, const std::wstring& url, bool isDocumentBrowser) | 
| +{ | 
| + std::wstring documentUrl = GetDocumentUrl(); | 
| + | 
| + if (isDocumentBrowser) | 
| + { | 
| + if (url != documentUrl) | 
| + { | 
| + SetDocumentUrl(url); | 
| + } | 
| + InjectABP(browser); | 
| + } | 
| + CString urlLegacy = ToCString(url); | 
| + if (urlLegacy.Left(6) != "res://") | 
| + { | 
| + // Get document | 
| + CComPtr<IDispatch> pDocDispatch; | 
| + HRESULT hr = browser->get_Document(&pDocDispatch); | 
| + if (FAILED(hr) || !pDocDispatch) | 
| + { | 
| + return; | 
| + } | 
| + | 
| + CComQIPtr<IHTMLDocument2> pDoc = pDocDispatch; | 
| + if (!pDoc) | 
| + { | 
| + return; | 
| + } | 
| + CComPtr<IOleObject> pOleObj; | 
| + | 
| + pDocDispatch->QueryInterface(IID_IOleObject, (void**)&pOleObj); | 
| + | 
| + | 
| + CComPtr<IOleClientSite> pClientSite; | 
| + pOleObj->GetClientSite(&pClientSite); | 
| + if (pClientSite != NULL) | 
| + { | 
| + CComPtr<IDocHostUIHandler> docHostUIHandler; | 
| + pClientSite->QueryInterface(IID_IDocHostUIHandler, (void**)&docHostUIHandler); | 
| + if (docHostUIHandler != NULL) | 
| + { | 
| + docHostUIHandler->UpdateUI(); | 
| + } | 
| + } | 
| + } | 
| +} | 
| + | 
| +std::wstring CPluginTabBase::GetDocumentDomain() | 
| +{ | 
| + std::wstring domain; | 
| + | 
| + m_criticalSection.Lock(); | 
| + { | 
| + domain = m_documentDomain; | 
| + } | 
| + m_criticalSection.Unlock(); | 
| + | 
| + return domain; | 
| +} | 
| + | 
| +void CPluginTabBase::SetDocumentUrl(const std::wstring& url) | 
| +{ | 
| + m_criticalSection.Lock(); | 
| + { | 
| + m_documentUrl = url; | 
| + m_documentDomain = CAdblockPlusClient::GetInstance()->GetHostFromUrl(url); | 
| + } | 
| + m_criticalSection.Unlock(); | 
| +} | 
| + | 
| +std::wstring CPluginTabBase::GetDocumentUrl() | 
| +{ | 
| + std::wstring url; | 
| + | 
| + m_criticalSection.Lock(); | 
| + { | 
| + url = m_documentUrl; | 
| + } | 
| + m_criticalSection.Unlock(); | 
| + | 
| + return url; | 
| +} | 
| + | 
| + | 
| +// ============================================================================ | 
| +// Frame caching | 
| +// ============================================================================ | 
| +bool CPluginTabBase::IsFrameCached(const std::wstring& url) | 
| +{ | 
| + bool isFrame; | 
| + | 
| + m_criticalSectionCache.Lock(); | 
| + { | 
| + isFrame = m_cacheFrames.find(url) != m_cacheFrames.end(); | 
| + } | 
| + m_criticalSectionCache.Unlock(); | 
| + | 
| + return isFrame; | 
| +} | 
| + | 
| +void CPluginTabBase::CacheFrame(const std::wstring& url) | 
| +{ | 
| + m_criticalSectionCache.Lock(); | 
| + { | 
| + m_cacheFrames.insert(url); | 
| + } | 
| + m_criticalSectionCache.Unlock(); | 
| +} | 
| + | 
| +void CPluginTabBase::ClearFrameCache(const std::wstring& domain) | 
| +{ | 
| + m_criticalSectionCache.Lock(); | 
| + { | 
| + if (domain.empty() || domain != m_cacheDomain) | 
| + { | 
| + m_cacheFrames.clear(); | 
| + m_cacheDomain = domain; | 
| + } | 
| + } | 
| + m_criticalSectionCache.Unlock(); | 
| +} | 
| + | 
| +void CPluginTabBase::ThreadProc() | 
| +{ | 
| + // Force loading/creation of settings | 
| + CPluginSettings::GetInstance()->SetWorkingThreadId(); | 
| + | 
| + std::string message = | 
| + "================================================================================\n" | 
| + "TAB THREAD process="; | 
| + message += std::to_string(::GetCurrentProcessId()); | 
| + message + " thread="; | 
| + message += std::to_string(::GetCurrentThreadId()); | 
| + message += | 
| + "\n" | 
| + "================================================================================"; | 
| + DEBUG_GENERAL(message); | 
| + | 
| + // -------------------------------------------------------------------- | 
| + // Tab loop | 
| + // -------------------------------------------------------------------- | 
| + | 
| + DWORD loopCount = 0; | 
| + DWORD tabLoopIteration = 1; | 
| + | 
| + while (this->m_continueThreadRunning) | 
| + { | 
| +#ifdef ENABLE_DEBUG_THREAD | 
| + CStringA sTabLoopIteration; | 
| + sTabLoopIteration.Format("%u", tabLoopIteration); | 
| + | 
| + DEBUG_THREAD("--------------------------------------------------------------------------------") | 
| + DEBUG_THREAD("Loop iteration " + sTabLoopIteration); | 
| + DEBUG_THREAD("--------------------------------------------------------------------------------") | 
| +#endif | 
| + this->m_isActivated = false; | 
| + | 
| + // -------------------------------------------------------------------- | 
| + // End loop | 
| + // -------------------------------------------------------------------- | 
| + | 
| + // Sleep loop | 
| + while (this->m_continueThreadRunning && !this->m_isActivated && (++loopCount % (TIMER_THREAD_SLEEP_TAB_LOOP / 50)) != 0) | 
| + { | 
| + // Post async plugin error | 
| + CPluginError pluginError; | 
| + if (LogQueue::PopFirstPluginError(pluginError)) | 
| + { | 
| + LogQueue::LogPluginError(pluginError.GetErrorCode(), pluginError.GetErrorId(), pluginError.GetErrorSubid(), pluginError.GetErrorDescription(), true, pluginError.GetProcessId(), pluginError.GetThreadId()); | 
| + } | 
| + | 
| + // Non-hanging sleep | 
| + Sleep(50); | 
| + } | 
| + | 
| + tabLoopIteration++; | 
| + } | 
| +} |