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

Side by Side Diff: src/engine/NotificationWindow.cpp

Issue 6505394822184960: Issue 1109 - Support notifications (Closed)
Patch Set: address comments and a couple of additional fixes Created Aug. 17, 2015, 1:11 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
« no previous file with comments | « src/engine/NotificationWindow.h ('k') | src/plugin/AdblockPlusClient.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2015 Eyeo GmbH
4 *
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
7 * published by the Free Software Foundation.
8 *
9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
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/>.
16 */
17
18 #include <cassert>
19 #include "NotificationWindow.h"
20 #include "../shared/Utils.h"
21 #include <algorithm>
22 #include <fstream>
23 #include "../shared/MsHTMLUtils.h"
24
25 // it is taken from src/plugin/Resource.h
26 #define IDI_ICON_ENABLED 301
27
28 namespace {
29 // DIP = device independant pixels, as DPI is 96
30 const uint32_t kWindowWidth = 400 /*DIP*/;
31 const uint32_t kWindowHeight = 120 /*DIP*/;
32 const uint32_t kIconSize = 64 /*DIP*/;
33 const uint32_t kIconPadding = 10 /*DIP*/;
34
35 // offsets from boundaries of working area of screen
36 const uint32_t kWindowMarginRight = 50 /*DIP*/;
37 const uint32_t kWindowMarginBottom = 20 /*DIP*/;
38
39 std::vector<uint16_t> iconSizes = []()->std::vector<uint16_t>
40 {
41 std::vector<uint16_t> iconSizes;
42 iconSizes.emplace_back(16);
43 iconSizes.emplace_back(19);
44 iconSizes.emplace_back(48);
45 iconSizes.emplace_back(128);
46 iconSizes.emplace_back(256);
47 return iconSizes;
48 }();
49
50 class DCHandle
51 {
52 public:
53 explicit DCHandle(HDC hDC = nullptr) : m_hDC(hDC)
54 {
55 }
56
57 ~DCHandle()
58 {
59 if(m_hDC != nullptr)
60 {
61 ::DeleteDC(m_hDC);
62 m_hDC = nullptr;
63 }
64 }
65
66 operator HDC()
67 {
68 return m_hDC;
69 }
70
71 int GetDeviceCaps(int nIndex) const
72 {
73 ATLASSERT(m_hDC != nullptr);
74 return ::GetDeviceCaps(m_hDC, nIndex);
75 }
76
77 HBITMAP SelectBitmap(HBITMAP value)
78 {
79 return static_cast<HBITMAP>(::SelectObject(m_hDC, value));
80 }
81 private:
82 DCHandle(const DCHandle&);
83 DCHandle& operator=(const DCHandle&);
84 private:
85 HDC m_hDC;
86 };
87
88 /// Case insensitive pattern replacing function.
89 /// ReplaceMulti("Some TeXt <PlaceHolder>Some link</A> and text", "<placeholde r>", ->"<a>")->
90 /// "Some TeXt <a>Some link</A> and text".
91 std::wstring ReplaceMulti(const std::wstring& src, std::wstring placeholder, c onst std::function<std::wstring()>& replacementGenerator)
92 {
93 std::transform(placeholder.begin(), placeholder.end(), placeholder.begin(), ::towlower);
94 std::wstring srcLowerCase = src;
95 std::transform(srcLowerCase.begin(), srcLowerCase.end(), srcLowerCase.begin( ), ::towlower);
96 std::wstring retValue;
97 std::wstring::size_type placeHolderOffset = 0;
98 std::wstring::size_type nextStringChunkOffset = 0;
99 while ((placeHolderOffset = srcLowerCase.find(placeholder, nextStringChunkOf fset)) != std::wstring::npos)
100 {
101 retValue.append(src.substr(nextStringChunkOffset, placeHolderOffset - next StringChunkOffset));
102 retValue.append(replacementGenerator());
103 nextStringChunkOffset = placeHolderOffset + placeholder.length();
104 }
105 retValue.append(src.substr(nextStringChunkOffset));
106 return retValue;
107 }
108 std::wstring ReplaceMulti(const std::wstring& workingString, const std::wstrin g& templ, const std::wstring& replacement)
109 {
110 return ReplaceMulti(workingString, templ, [&replacement]()->std::wstring
111 {
112 return replacement;
113 });
114 }
115 }
116
117 NotificationWindow::NotificationWindow(const AdblockPlus::Notification& notifica tion, const std::wstring& htmlFileDir)
118 {
119 const std::wstring filePath = htmlFileDir + L"NotificationWindow.html";
120 std::wifstream ifs(filePath);
121 assert(ifs.good() && "Cannot open NotificationWindow.html file");
122 if (!ifs.good())
123 {
124 throw std::runtime_error("Cannot read NotificationWindow.html");
125 }
126 m_htmlPage.assign((std::istreambuf_iterator<wchar_t>(ifs)), std::istreambuf_it erator<wchar_t>());
127
128 m_links = ToUtf16Strings(notification.GetLinks());
129 auto body = ToUtf16String(notification.GetMessageString());
130 uint32_t linkIDCounter = 0;
131 body = ReplaceMulti(body, L"<a>", [this, &linkIDCounter]()->std::wstring
132 {
133 return L"<a href=\"#\" data-linkID=\"" + std::to_wstring(linkIDCounter++) + L"\">";
134 });
135 assert(linkIDCounter == m_links.size() && "The amount of links in the text is different from the amount of provided links");
136 m_htmlPage = ReplaceMulti(m_htmlPage, L"<!--Title-->", ToUtf16String(notificat ion.GetTitle()));
137 m_htmlPage = ReplaceMulti(m_htmlPage, L"<!--Body-->", body);
138 }
139
140 NotificationWindow::~NotificationWindow()
141 {
142 }
143
144 LRESULT NotificationWindow::OnCreate(const CREATESTRUCT* /*createStruct*/) {
145 m_bgColor.CreateSolidBrush(RGB(255, 255, 255));
146
147 CRect iconRect(CPoint(0, 0), CSize(kIconSize + 2 * kIconPadding, kIconSize + 2 * kIconPadding));
148 m_icon.Create(m_hWnd, DPIAware(iconRect), nullptr, WS_CHILD | WS_VISIBLE | SS_ BITMAP | SS_CENTERIMAGE);
149 LoadABPIcon();
150
151 m_axIE.Create(m_hWnd, DPIAware(CRect(CPoint(iconRect.right, 0), CSize(kWindowW idth - iconRect.right, kWindowHeight))),
152 L"", WS_CHILD | WS_VISIBLE, 0, kHTMLDocumentCtrlID);
153 m_axIE.CreateControl((L"mshtml:" + m_htmlPage).c_str());
154 ATL::CComPtr<IAxWinAmbientDispatch> axWinAmbient;
155 if (SUCCEEDED(m_axIE.QueryHost(&axWinAmbient))) {
156 // disable web browser context menu
157 axWinAmbient->put_AllowContextMenu(VARIANT_FALSE);
158 // make web browser DPI aware, so the browser itself sets zoom level and
159 // cares about rendering (not zooming) in the proper size.
160 DWORD docFlags;
161 axWinAmbient->get_DocHostFlags(&docFlags);
162 docFlags |= DOCHOSTUIFLAG_DPI_AWARE;
163 // remove DOCHOSTUIFLAG_SCROLL_NO, so it's scrollable
164 docFlags &= ~DOCHOSTUIFLAG_SCROLL_NO;
165 axWinAmbient->put_DocHostFlags(docFlags);
166 }
167 // kHTMLDocumentCtrlID works here
168 AtlAdviseSinkMap(this, true);
169
170 SetMsgHandled(false);
171 return 0;
172 }
173
174 LRESULT NotificationWindow::OnCtlColor(UINT /*msg*/, WPARAM /*wParam*/, LPARAM l Param, BOOL& handled)
175 {
176 if (reinterpret_cast<HWND>(lParam) != m_icon)
177 {
178 handled = FALSE;
179 }
180 return reinterpret_cast<LRESULT>(static_cast<HBRUSH>(m_bgColor));
181 }
182
183 LRESULT NotificationWindow::OnClick(UINT /*msg*/, WPARAM /*wParam*/, LPARAM /*lP aram*/, BOOL& /*handled*/)
184 {
185 if(m_onClickCallback)
186 m_onClickCallback();
187 return 0;
188 }
189
190 void NotificationWindow::OnSize(uint32_t wParam, CSize size)
191 {
192 if (m_icon)
193 {
194 CRect rect(CPoint(0, 0), DPIAware(CSize(kIconSize + 2 * kIconPadding, kIconS ize + 2 * kIconPadding)));
195 m_icon.SetWindowPos(0, &rect, SWP_NOMOVE);
196 }
197 if (m_axIE)
198 {
199 size.cx -= DPIAware(kIconSize + 2 * kIconPadding);
200 CRect rect(CPoint(0, 0), size);
201 m_axIE.SetWindowPos(0, &rect, SWP_NOMOVE);
202 }
203 SetMsgHandled(false);
204 }
205
206 void NotificationWindow::OnDestroy()
207 {
208 AtlAdviseSinkMap(this, false);
209 // and proceed as usual
210 SetMsgHandled(false);
211 }
212
213 void __stdcall NotificationWindow::OnHTMLDocumentClick(IHTMLEventObj* eventObjec t)
214 {
215 // stop propagating the event since it's handled by us and should not cause an y other actions.
216 if (!eventObject)
217 return;
218 eventObject->put_cancelBubble(VARIANT_TRUE);
219 eventObject->put_returnValue(ATL::CComVariant(false));
220 ATL::CComPtr<IHTMLElement> htmlElement;
221 if (FAILED(eventObject->get_srcElement(&htmlElement)) || !htmlElement) {
222 return;
223 }
224 ATL::CComBSTR tag;
225 htmlElement->get_tagName(&tag);
226 const wchar_t expectedTag[] = { L"a" };
227 if (_wcsnicmp(tag, expectedTag, min(sizeof(expectedTag), tag.Length())) != 0) {
228 return;
229 }
230 auto classAttr = GetHtmlElementAttribute(*htmlElement, ATL::CComBSTR(L"class") );
231 if (classAttr.attributeValue == L"closeButton")
232 {
233 if (m_onCloseCallback)
234 m_onCloseCallback();
235 return;
236 }
237 if (!m_onLinkClickedCallback)
238 {
239 return;
240 }
241 auto linkIDAttr = GetHtmlElementAttribute(*htmlElement, ATL::CComBSTR(L"data-l inkID"));
242 uint32_t linkID = 0;
243 if (!linkIDAttr.attributeValue.empty() && (linkID = std::stoi(linkIDAttr.attri buteValue)) < m_links.size())
244 {
245 m_onLinkClickedCallback(m_links[linkID]);
246 if (m_onCloseCallback)
247 m_onCloseCallback();
248 }
249 }
250
251 void __stdcall NotificationWindow::OnHTMLDocumentSelectStart(IHTMLEventObj* even tObject)
252 {
253 if (!eventObject)
254 return;
255 // disable selecting
256 eventObject->put_cancelBubble(VARIANT_TRUE);
257 eventObject->put_returnValue(ATL::CComVariant(false));
258 }
259
260 void NotificationWindow::LoadABPIcon()
261 {
262 ScopedModule m_adblockPlusDLL;
263 if (!(m_adblockPlusDLL.Open(L"AdblockPlus32.dll", LOAD_LIBRARY_AS_DATAFILE) ||
264 m_adblockPlusDLL.Open(L"AdblockPlus64.dll", LOAD_LIBRARY_AS_DATAFILE) ||
265 // for debug
266 m_adblockPlusDLL.Open(L"AdblockPlus.dll", LOAD_LIBRARY_AS_DATAFILE)))
267 {
268 return;
269 }
270 auto iconSizeIterator = lower_bound(iconSizes.begin(), iconSizes.end(), DPIAwa re(kIconSize));
271 if (iconSizeIterator == iconSizes.end())
272 {
273 iconSizeIterator = iconSizes.rbegin().base();
274 }
275
276 auto hIcon = static_cast<HICON>(::LoadImageW(m_adblockPlusDLL, (L"#" + std::to _wstring(IDI_ICON_ENABLED)).c_str(),
277 IMAGE_ICON, *iconSizeIterator, *iconSizeIterator, LR_SHARED));
278 if (hIcon == nullptr)
279 {
280 return;
281 }
282
283 HDC screenDC = m_icon.GetDC();
284 DCHandle tmpDC(::CreateCompatibleDC(screenDC));
285 ScopedObjectHandle<HBITMAP> bitmap;
286 bitmap = CreateCompatibleBitmap(screenDC, DPIAware(kIconSize), DPIAware(kIconS ize));
287 HBITMAP prevBitmap = tmpDC.SelectBitmap(bitmap);
288 CRect tmpRect(CPoint(0, 0), DPIAware(CSize(kIconSize, kIconSize)));
289 FillRect(tmpDC, &tmpRect, m_bgColor);
290 ::DrawIconEx(tmpDC, 0, 0, hIcon, tmpRect.Width(), tmpRect.Height(), 0, nullptr , DI_NORMAL);
291 m_iconImg.Attach(bitmap.Detach());
292 tmpDC.SelectBitmap(prevBitmap);
293 m_icon.SetBitmap(m_iconImg);
294 }
295
296 POINT NotificationBorderWindow::GetWindowCoordinates() {
297 HMONITOR primaryMonitor = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTOPRIMARY);
298 {
299 DCHandle hdc(GetDC());
300 m_dpi = hdc.GetDeviceCaps(LOGPIXELSX);
301 }
302 MONITORINFO monitorInfo = {};
303 monitorInfo.cbSize = sizeof(monitorInfo);
304 GetMonitorInfo(primaryMonitor, &monitorInfo);
305 int windowX = monitorInfo.rcWork.right - DPIAware(kWindowWidth + kWindowMargin Right);
306 int windowY = monitorInfo.rcWork.bottom - DPIAware(kWindowHeight + kWindowMarg inBottom);
307 POINT coords = {windowX, windowY};
308 return coords;
309 }
310
311 NotificationBorderWindow::NotificationBorderWindow(const AdblockPlus::Notificati on& notification, const std::wstring& htmlFileDir)
312 : m_content(notification, htmlFileDir)
313 {
314 m_content.SetOnClick([this]
315 {
316 PostMessage(WM_CLOSE);
317 });
318 m_content.SetOnClose([this]
319 {
320 PostMessage(WM_CLOSE);
321 });
322 }
323
324 LRESULT NotificationBorderWindow::OnCreate(const CREATESTRUCT* createStruct)
325 {
326 auto windowCoords = GetWindowCoordinates();
327 MoveWindow(windowCoords.x, windowCoords.y, DPIAware(kWindowWidth), DPIAware(kW indowHeight));
328
329 RECT clientRect;
330 GetClientRect(&clientRect);
331 // make one pixel border
332 clientRect.top += 1;
333 clientRect.left += 1;
334 clientRect.bottom -= 1;
335 clientRect.right -= 1;
336 m_content.Create(m_hWnd, clientRect, nullptr, WS_CHILD | WS_VISIBLE);
337 auto err = GetLastError();
338 SetMsgHandled(false);
339 return 0;
340 }
341
342 void NotificationBorderWindow::OnSize(uint32_t wParam, CSize size)
343 {
344 if (m_content.IsWindow())
345 {
346 RECT clientRect;
347 GetClientRect(&clientRect);
348 clientRect.top += 1;
349 clientRect.left += 1;
350 clientRect.bottom -= 1;
351 clientRect.right -= 1;
352 m_content.MoveWindow(&clientRect);
353 }
354 SetMsgHandled(false);
355 }
356
357 LRESULT NotificationBorderWindow::OnClick(UINT /*msg*/, WPARAM /*wParam*/, LPARA M /*lParam*/, BOOL& /*handled*/)
358 {
359 DestroyWindow();
360 return 0;
361 }
362
363 void NotificationBorderWindow::OnFinalMessage(HWND) {
364 if (!!m_onDestroyedCallback) {
365 m_onDestroyedCallback();
366 }
367 }
OLDNEW
« no previous file with comments | « src/engine/NotificationWindow.h ('k') | src/plugin/AdblockPlusClient.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld