Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: src/plugin/PluginTabBase.cpp

Issue 6567422169448448: Issue 119 - Switch to injecting CSS for element hiding (Closed)
Patch Set: rebase, improve memory handling (OnQuit) and improve code organizing Created April 27, 2016, 1:10 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2016 Eyeo GmbH 3 * Copyright (C) 2006-2016 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 #include "PluginStdAfx.h" 18 #include "PluginStdAfx.h"
19 #include "AdblockPlusClient.h" 19 #include "AdblockPlusClient.h"
20 #include "PluginClientBase.h" 20 #include "PluginClientBase.h"
21 #include "PluginSettings.h" 21 #include "PluginSettings.h"
22 #include "AdblockPlusDomTraverser.h" 22 #include "AdblockPlusDomTraverser.h"
23 #include "PluginTabBase.h" 23 #include "PluginTabBase.h"
24 #include "IeVersion.h" 24 #include "IeVersion.h"
25 #include "../shared/Utils.h" 25 #include "../shared/Utils.h"
26 #include "../shared/EventWithSetter.h"
26 #include <Mshtmhst.h> 27 #include <Mshtmhst.h>
28 #include <mutex>
29
30 class CPluginTab::AsyncPluginFilter
31 {
32 public:
33 static std::shared_ptr<AsyncPluginFilter> CreateAsync(const std::wstring& doma in)
34 {
35 std::shared_ptr<AsyncPluginFilter> asyncFilter = std::make_shared<AsyncPlugi nFilter>();
36 std::weak_ptr<AsyncPluginFilter> weakAsyncData = asyncFilter;
37 auto eventSetter = asyncFilter->event.CreateSetter();
38 try
39 {
40 std::thread([domain, weakAsyncData, eventSetter]
41 {
42 try
43 {
44 CreateAsyncImpl(domain, weakAsyncData, eventSetter);
45 }
46 catch (...)
47 {
48 // As a thread-main function, we truncate any C++ exception.
49 }
50 }).detach();
51 // TODO: we should do something with that `detach` above.
52 }
53 catch (const std::system_error& ex)
54 {
55 DEBUG_SYSTEM_EXCEPTION(ex, PLUGIN_ERROR_THREAD, PLUGIN_ERROR_MAIN_THREAD_C REATE_PROCESS,
56 "Class::Thread - Failed to start filter loader thread");
57 }
58 return asyncFilter;
59 }
60 PluginFilterPtr GetFilter()
61 {
62 if (!event.Wait())
63 return PluginFilterPtr();
64 std::lock_guard<std::mutex> lock(mutex);
65 return filter;
66 }
67 private:
68 static void CreateAsyncImpl(const std::wstring& domain, std::weak_ptr<AsyncPlu ginFilter> weakAsyncData, const std::shared_ptr<EventWithSetter::Setter>& setter )
69 {
70 std::unique_ptr<CPluginFilter> pluginFilter(new CPluginFilter(CPluginClient: :GetInstance()->GetElementHidingSelectors(domain)));
71 if (auto asyncData = weakAsyncData.lock())
72 {
73 {
74 std::lock_guard<std::mutex> lock(asyncData->mutex);
75 asyncData->filter = move(pluginFilter);
76 }
77 setter->Set();
78 }
79 }
80 EventWithSetter event;
81 std::mutex mutex;
82 PluginFilterPtr filter;
83 };
27 84
28 CPluginTab::CPluginTab() 85 CPluginTab::CPluginTab()
29 : m_isActivated(false) 86 : m_isActivated(false)
30 , m_continueThreadRunning(true) 87 , m_continueThreadRunning(true)
31 { 88 {
32 m_filter.hideFiltersLoadedEvent = CreateEvent(NULL, true, false, NULL);
33
34 CPluginClient* client = CPluginClient::GetInstance(); 89 CPluginClient* client = CPluginClient::GetInstance();
35 if (AdblockPlus::IE::InstalledMajorVersion() < 10) 90 if (AdblockPlus::IE::InstalledMajorVersion() < 10)
36 { 91 {
37 m_isActivated = true; 92 m_isActivated = true;
38 } 93 }
39 94
40 try 95 try
41 { 96 {
42 m_thread = std::thread(&CPluginTab::ThreadProc, this); 97 m_thread = std::thread(&CPluginTab::ThreadProc, this);
43 } 98 }
44 catch (const std::system_error& ex) 99 catch (const std::system_error& ex)
45 { 100 {
46 DEBUG_SYSTEM_EXCEPTION(ex, PLUGIN_ERROR_THREAD, PLUGIN_ERROR_TAB_THREAD_CREA TE_PROCESS, 101 DEBUG_SYSTEM_EXCEPTION(ex, PLUGIN_ERROR_THREAD, PLUGIN_ERROR_TAB_THREAD_CREA TE_PROCESS,
47 "Tab::Thread - Failed to create tab thread"); 102 "Tab::Thread - Failed to create tab thread");
48 } 103 }
49 m_traverser = new CPluginDomTraverser(static_cast<CPluginTab*>(this));
50 } 104 }
51 105
52 106
53 CPluginTab::~CPluginTab() 107 CPluginTab::~CPluginTab()
54 { 108 {
55 delete m_traverser;
56 m_traverser = NULL;
57 m_continueThreadRunning = false; 109 m_continueThreadRunning = false;
58 if (m_thread.joinable()) { 110 if (m_thread.joinable()) {
59 m_thread.join(); 111 m_thread.join();
60 } 112 }
61 } 113 }
62 114
63 /** 115 /**
64 * ABP only intercepts protocols "http:" and "https:". 116 * ABP only intercepts protocols "http:" and "https:".
65 * We can disable any domain used in those protocol with an appropriate whitelis t filter. 117 * We can disable any domain used in those protocol with an appropriate whitelis t filter.
66 * Thus, the possibility to disable on a particular site depends only on the pro tocol. 118 * Thus, the possibility to disable on a particular site depends only on the pro tocol.
67 */ 119 */
68 bool CPluginTab::IsPossibleToDisableOnSite() 120 bool CPluginTab::IsPossibleToDisableOnSite()
69 { 121 {
70 auto url = GetDocumentUrl(); 122 auto url = GetDocumentUrl();
71 return BeginsWith(url, L"http:") || BeginsWith(url, L"https:"); 123 return BeginsWith(url, L"http:") || BeginsWith(url, L"https:");
72 } 124 }
73 125
74 void CPluginTab::OnActivate() 126 void CPluginTab::OnActivate()
75 { 127 {
76 m_isActivated = true; 128 m_isActivated = true;
77 } 129 }
78 130
79 131
80 void CPluginTab::OnUpdate() 132 void CPluginTab::OnUpdate()
81 { 133 {
82 m_isActivated = true; 134 m_isActivated = true;
83 } 135 }
84 136
85 namespace
86 {
87 // Entry Point
88 void FilterLoader(CPluginFilter* filter, const std::wstring& domain)
89 {
90 try
91 {
92 filter->LoadHideFilters(CPluginClient::GetInstance()->GetElementHidingSele ctors(domain));
93 SetEvent(filter->hideFiltersLoadedEvent);
94 }
95 catch (...)
96 {
97 // As a thread-main function, we truncate any C++ exception.
98 }
99 }
100 }
101
102 void CPluginTab::OnNavigate(const std::wstring& url) 137 void CPluginTab::OnNavigate(const std::wstring& url)
103 { 138 {
104 SetDocumentUrl(url); 139 SetDocumentUrl(url);
105 ClearFrameCache(GetDocumentDomain()); 140 std::wstring domain = GetDocumentDomain();
106 std::wstring domainString = GetDocumentDomain(); 141 ClearFrameCache(domain);
107 ResetEvent(m_filter.hideFiltersLoadedEvent); 142 m_asyncPluginFilter = AsyncPluginFilter::CreateAsync(domain);
108 try 143 m_traverser.reset();
109 {
110 std::thread filterLoaderThread(&FilterLoader, &m_filter, GetDocumentDomain() );
111 filterLoaderThread.detach(); // TODO: but actually we should wait for the th read in the dtr.
112 }
113 catch (const std::system_error& ex)
114 {
115 DEBUG_SYSTEM_EXCEPTION(ex, PLUGIN_ERROR_THREAD, PLUGIN_ERROR_MAIN_THREAD_CRE ATE_PROCESS,
116 "Class::Thread - Failed to start filter loader thread");
117 }
118 m_traverser->ClearCache();
119 } 144 }
120 145
121 namespace 146 namespace
122 { 147 {
123 /** 148 /**
124 * Determine if the HTML file is one of ours. 149 * Determine if the HTML file is one of ours.
125 * The criterion is that it appear in the "html/templates" folder within our i nstallation. 150 * The criterion is that it appear in the "html/templates" folder within our i nstallation.
126 * 151 *
127 * Warning: This function may fail if the argument is not a "file://" URL. 152 * Warning: This function may fail if the argument is not a "file://" URL.
128 * This is occasionally the case in circumstances yet to be characterized. 153 * This is occasionally the case in circumstances yet to be characterized.
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 params.rgvarg = &var; 231 params.rgvarg = &var;
207 params.rgdispidNamedArgs = 0; 232 params.rgdispidNamedArgs = 0;
208 hr = pWndEx->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPU T | DISPATCH_PROPERTYPUTREF, &params, 0, 0, 0); 233 hr = pWndEx->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPU T | DISPATCH_PROPERTYPUTREF, &params, 0, 0, 0);
209 DEBUG_GENERAL("Invoke"); 234 DEBUG_GENERAL("Invoke");
210 if (FAILED(hr)) 235 if (FAILED(hr))
211 { 236 {
212 DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT, PLUGIN_ERROR_CR EATE_SETTINGS_JAVASCRIPT_INVOKE, "CPluginTab::InjectABP - Failed to create Setti ngs in JavaScript"); 237 DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_CREATE_SETTINGS_JAVASCRIPT, PLUGIN_ERROR_CR EATE_SETTINGS_JAVASCRIPT_INVOKE, "CPluginTab::InjectABP - Failed to create Setti ngs in JavaScript");
213 } 238 }
214 } 239 }
215 240
241 bool CPluginTab::IsTraverserEnabled()
242 {
243 return !IsCSSInjectionEnabled();
244 }
245
246 bool CPluginTab::IsCSSInjectionEnabled()
247 {
248 return IsWindowsVistaOrLater() && AdblockPlus::IE::InstalledMajorVersion() >= 10;
249 }
250
251 namespace
252 {
253 void InjectABPCSS(IHTMLDocument2& htmlDocument2, const std::vector<std::wstrin g>& hideFilters)
254 {
255 // pseudocode: styleHtmlElement = htmlDocument2.createElement("style");
256 ATL::CComQIPtr<IHTMLStyleElement> styleHtmlElement;
257 {
258 ATL::CComPtr<IHTMLElement> stylePureHtmlElement;
259 if (FAILED(htmlDocument2.createElement(ATL::CComBSTR(L"style"), &stylePure HtmlElement)))
260 {
261 DEBUG_GENERAL(L"Cannot create style element");
262 return;
263 }
264 if (!(styleHtmlElement = stylePureHtmlElement))
265 {
266 DEBUG_GENERAL(L"Cannot obtain IHTMLStyleElement from IHTMLElement");
267 return;
268 }
269 }
270 // pseudocode: styleHtmlElement.type = "text/css";
271 if (FAILED(styleHtmlElement->put_type(ATL::CComBSTR("text/css"))))
272 {
273 DEBUG_GENERAL(L"Cannot set type text/css");
274 return;
275 }
276 // pseudocode: styleSheet4 = styleHtmlElement.sheet;
277 ATL::CComQIPtr<IHTMLStyleSheet4> styleSheet4;
278 {
279 // IHTMLStyleElement2 is availabe starting from IE9, Vista
280 ATL::CComQIPtr<IHTMLStyleElement2> styleHtmlElement2 = styleHtmlElement;
281 if (!styleHtmlElement2)
282 {
283 DEBUG_GENERAL(L"Cannot obtain IHTMLStyleElement2 from IHTMLStyleElement" );
284 return;
285 }
286 ATL::CComQIPtr<IHTMLStyleSheet> styleSheet;
287 if (FAILED(styleHtmlElement2->get_sheet(&styleSheet)) || !styleSheet)
288 {
289 DEBUG_GENERAL(L"Cannot obtain IHTMLStyleSheet");
290 return;
291 }
292 // IHTMLStyleSheet4 is availabe starting from IE9, Vista
293 styleSheet4 = styleSheet;
294 if (!styleSheet4)
295 {
296 DEBUG_GENERAL(L"Cannot obtain IHTMLStyleSheet4");
297 return;
298 }
299 }
300 // pseudocode: for (auto i = 0; i < hideFilters.length; ++i) {
301 // pseudocode: i = styleSheet4.insertRule(hideFilters + cssValue, i);
302 // pseudocode: }
303 long newIndex = 0;
304 std::wstring cssValue = L"{ display: none !important; }";
305 for (const auto& selector : hideFilters)
306 {
307 auto cssRule = selector + cssValue;
308 ATL::CComBSTR selector(cssRule.size(), cssRule.c_str());
309 if (SUCCEEDED(styleSheet4->insertRule(selector, newIndex, &newIndex)))
310 {
311 ++newIndex;
312 }
313 else
314 {
315 DEBUG_GENERAL(L"Cannot add rule for selector " + cssRule);
316 }
317 }
318
319 // pseudocode: htmlDocument2.head.appendChild(styleHtmlElement);
320 {
321 // IHTMLDocument7 is availabe starting from IE9, Vista
322 ATL::CComQIPtr<IHTMLDocument7> htmlDocument7 = &htmlDocument2;
323 if (!htmlDocument7)
324 {
325 DEBUG_GENERAL(L"Cannot obtain IHTMLDocument7 from htmlDocument2");
326 return;
327 }
328 ATL::CComPtr<IHTMLElement> headHtmlElement;
329 if (FAILED(htmlDocument7->get_head(&headHtmlElement)))
330 {
331 DEBUG_GENERAL(L"Cannot obtain head from pDoc7");
332 return;
333 }
334 ATL::CComQIPtr<IHTMLDOMNode> headNode = headHtmlElement;
335 if (!headNode)
336 {
337 DEBUG_GENERAL(L"Cannot obtain headNode from headHtmlElement");
338 return;
339 }
340 ATL::CComQIPtr<IHTMLDOMNode> styleNode = styleHtmlElement;
341 if (!styleNode)
342 {
343 DEBUG_GENERAL(L"Cannot obtain IHTMLDOMNode from stylePureHtmlElement");
344 return;
345 }
346 if (FAILED(headNode->appendChild(styleNode, nullptr)))
347 {
348 DEBUG_GENERAL(L"Cannot append blocking style");
349 }
350 }
351 }
352 }
353
216 namespace 354 namespace
217 { 355 {
218 ATL::CComPtr<IWebBrowser2> GetParent(IWebBrowser2& browser) 356 ATL::CComPtr<IWebBrowser2> GetParent(IWebBrowser2& browser)
219 { 357 {
220 ATL::CComPtr<IDispatch> parentDispatch; 358 ATL::CComPtr<IDispatch> parentDispatch;
221 if (FAILED(browser.get_Parent(&parentDispatch)) || !parentDispatch) 359 if (FAILED(browser.get_Parent(&parentDispatch)) || !parentDispatch)
222 { 360 {
223 return nullptr; 361 return nullptr;
224 } 362 }
225 // The InternetExplorer application always returns a pointer to itself. 363 // The InternetExplorer application always returns a pointer to itself.
(...skipping 28 matching lines...) Expand all
254 frameHierarchy.push_back(ToUtf8String(GetLocationUrl(*frame))); 392 frameHierarchy.push_back(ToUtf8String(GetLocationUrl(*frame)));
255 } 393 }
256 CPluginClient* client = CPluginClient::GetInstance(); 394 CPluginClient* client = CPluginClient::GetInstance();
257 return client->IsWhitelistedUrl(url, frameHierarchy) 395 return client->IsWhitelistedUrl(url, frameHierarchy)
258 || client->IsElemhideWhitelistedOnDomain(url, frameHierarchy); 396 || client->IsElemhideWhitelistedOnDomain(url, frameHierarchy);
259 } 397 }
260 } 398 }
261 399
262 void CPluginTab::OnDownloadComplete(IWebBrowser2* browser) 400 void CPluginTab::OnDownloadComplete(IWebBrowser2* browser)
263 { 401 {
264 CPluginClient* client = CPluginClient::GetInstance(); 402 if (IsTraverserEnabled())
265 std::wstring url = GetDocumentUrl();
266 if (!client->IsWhitelistedUrl(url) && !client->IsElemhideWhitelistedOnDomain(u rl))
267 { 403 {
268 m_traverser->TraverseDocument(browser, GetDocumentDomain(), GetDocumentUrl() ); 404 CPluginClient* client = CPluginClient::GetInstance();
405 std::wstring url = GetDocumentUrl();
406 if (!client->IsWhitelistedUrl(url) && !client->IsElemhideWhitelistedOnDomain (url))
407 {
408 if (!m_traverser)
409 {
410 assert(m_asyncPluginFilter && "Filter initialization should be already a t least started");
411 if (m_asyncPluginFilter)
412 {
413 auto pluginFilter = m_asyncPluginFilter->GetFilter();
414 assert(pluginFilter && "Plugin filter should be a valid object");
415 if (pluginFilter)
416 m_traverser.reset(new CPluginDomTraverser(pluginFilter));
417 }
418 }
419 assert(m_traverser && "Traverser should be a valid object");
420 if (m_traverser)
421 m_traverser->TraverseDocument(browser, GetDocumentDomain(), GetDocumentU rl());
422 }
269 } 423 }
270 InjectABP(browser); 424 InjectABP(browser);
271 } 425 }
272 426
273 void CPluginTab::OnDocumentComplete(IWebBrowser2* browser, const std::wstring& u rl, bool isDocumentBrowser) 427 void CPluginTab::OnDocumentComplete(IWebBrowser2* browser, const std::wstring& u rl, bool isDocumentBrowser)
274 { 428 {
275 std::wstring documentUrl = GetDocumentUrl(); 429 std::wstring documentUrl = GetDocumentUrl();
276 430
277 if (isDocumentBrowser) 431 if (isDocumentBrowser)
278 { 432 {
(...skipping 14 matching lines...) Expand all
293 { 447 {
294 return; 448 return;
295 } 449 }
296 450
297 CComQIPtr<IHTMLDocument2> pDoc(pDocDispatch); 451 CComQIPtr<IHTMLDocument2> pDoc(pDocDispatch);
298 if (!pDoc) 452 if (!pDoc)
299 { 453 {
300 return; 454 return;
301 } 455 }
302 456
457 if (IsCSSInjectionEnabled() && CPluginSettings::GetInstance()->GetPluginEnable d())
458 {
459 if (!IsFrameWhiteListed(browser))
460 {
461 DEBUG_GENERAL(L"Inject CSS into " + url);
462 assert(m_asyncPluginFilter && "Filter initialization should be already at least started");
463 if (m_asyncPluginFilter)
464 {
465 auto pluginFilter = m_asyncPluginFilter->GetFilter();
466 assert(pluginFilter && "Plugin filter should be a valid object");
467 if (pluginFilter)
468 {
469 InjectABPCSS(*pDoc, pluginFilter->GetHideFilters());
470 }
471 }
472 }
473 }
474
303 CComPtr<IOleObject> pOleObj; 475 CComPtr<IOleObject> pOleObj;
304 pDocDispatch->QueryInterface(&pOleObj); 476 pDocDispatch->QueryInterface(&pOleObj);
305 if (!pOleObj) 477 if (!pOleObj)
306 { 478 {
307 return; 479 return;
308 } 480 }
309 CComPtr<IOleClientSite> pClientSite; 481 CComPtr<IOleClientSite> pClientSite;
310 pOleObj->GetClientSite(&pClientSite); 482 pOleObj->GetClientSite(&pClientSite);
311 if (pClientSite != NULL) 483 if (pClientSite != NULL)
312 { 484 {
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
443 LogQueue::LogPluginError(pluginError.GetErrorCode(), pluginError.GetEr rorId(), pluginError.GetErrorSubid(), pluginError.GetErrorDescription(), true, p luginError.GetProcessId(), pluginError.GetThreadId()); 615 LogQueue::LogPluginError(pluginError.GetErrorCode(), pluginError.GetEr rorId(), pluginError.GetErrorSubid(), pluginError.GetErrorDescription(), true, p luginError.GetProcessId(), pluginError.GetThreadId());
444 } 616 }
445 617
446 // Non-hanging sleep 618 // Non-hanging sleep
447 Sleep(50); 619 Sleep(50);
448 } 620 }
449 621
450 tabLoopIteration++; 622 tabLoopIteration++;
451 } 623 }
452 } 624 }
OLDNEW

Powered by Google App Engine
This is Rietveld