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

Delta Between Two Patch Sets: src/engine/NotificationWindow.cpp

Issue 6505394822184960: Issue 1109 - Support notifications (Closed)
Left Patch Set: Created May 11, 2015, 10:01 a.m.
Right Patch Set: fix obtaining of DPI value of content (NotificationWindow) of NotificationBorderWindow Created Aug. 18, 2015, 9:43 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
LEFTRIGHT
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
1 #include <cassert> 18 #include <cassert>
2 #include "NotificationWindow.h" 19 #include "NotificationWindow.h"
3 #include "../shared/Utils.h" 20 #include "../shared/Utils.h"
4 #include <algorithm> 21 #include <algorithm>
5 #include <fstream> 22 #include <fstream>
6 #include "../shared/MsHTMLUtils.h" 23 #include "../shared/MsHTMLUtils.h"
7 24
8 // it is taken from src/plugin/Resource.h 25 // it is taken from src/plugin/Resource.h
9 #define IDI_ICON_ENABLED 301 26 #define IDI_ICON_ENABLED 301
10 27
11 namespace { 28 namespace {
12 // DIP = device independant pixels, as DPI is 96 29 // DIP = device independant pixels, as DPI is 96
13 const uint32_t kWindowWidth = 400 /*DIP*/; 30 const uint32_t kWindowWidth = 400 /*DIP*/;
14 const uint32_t kWindowHeight = 120 /*DIP*/; 31 const uint32_t kWindowHeight = 120 /*DIP*/;
15 const uint32_t kIconSize = 64 /*DIP*/; 32 const uint32_t kIconSize = 64 /*DIP*/;
33 const uint32_t kIconPadding = 10 /*DIP*/;
16 34
17 // offsets from boundaries of working area of screen 35 // offsets from boundaries of working area of screen
18 const uint32_t kWindowMarginRight = 50 /*DIP*/; 36 const uint32_t kWindowMarginRight = 50 /*DIP*/;
19 const uint32_t kWindowMarginBottom = 20 /*DIP*/; 37 const uint32_t kWindowMarginBottom = 20 /*DIP*/;
20 38
21 std::vector<uint16_t> iconSizes = []()->std::vector<uint16_t> 39 std::vector<uint16_t> iconSizes = []()->std::vector<uint16_t>
22 { 40 {
23 std::vector<uint16_t> iconSizes; 41 std::vector<uint16_t> iconSizes;
24 iconSizes.emplace_back(16); 42 iconSizes.emplace_back(16);
25 iconSizes.emplace_back(19); 43 iconSizes.emplace_back(19);
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
60 { 78 {
61 return static_cast<HBITMAP>(::SelectObject(m_hDC, value)); 79 return static_cast<HBITMAP>(::SelectObject(m_hDC, value));
62 } 80 }
63 private: 81 private:
64 DCHandle(const DCHandle&); 82 DCHandle(const DCHandle&);
65 DCHandle& operator=(const DCHandle&); 83 DCHandle& operator=(const DCHandle&);
66 private: 84 private:
67 HDC m_hDC; 85 HDC m_hDC;
68 }; 86 };
69 87
70 std::wstring ReplaceMulti(std::wstring workingString, const std::wstring& plac eholder, const std::function<std::wstring()>& replacementGenerator) 88 /// Case insensitive pattern replacing function.
71 { 89 /// ReplaceMulti("Some TeXt <PlaceHolder>Some link</A> and text", "<placeholde r>", ->"<a>")->
72 std::wstring::size_type i = 0; 90 /// "Some TeXt <a>Some link</A> and text".
73 while ((i = workingString.find(placeholder, i)) != std::wstring::npos) 91 std::wstring ReplaceMulti(const std::wstring& src, std::wstring placeholder, c onst std::function<std::wstring()>& replacementGenerator)
74 { 92 {
75 const std::wstring dst = replacementGenerator(); 93 std::transform(placeholder.begin(), placeholder.end(), placeholder.begin(), ::towlower);
76 workingString.replace(i, placeholder.length(), dst); 94 std::wstring srcLowerCase = src;
77 // tiny optimisation to skip already processed part of the string 95 std::transform(srcLowerCase.begin(), srcLowerCase.end(), srcLowerCase.begin( ), ::towlower);
78 i += dst.length(); 96 std::wstring retValue;
79 } 97 std::wstring::size_type placeHolderOffset = 0;
80 return workingString; 98 std::wstring::size_type nextStringChunkOffset = 0;
81 } 99 while ((placeHolderOffset = srcLowerCase.find(placeholder, nextStringChunkOf fset)) != std::wstring::npos)
82 std::wstring ReplaceMulti(std::wstring workingString, const std::wstring& temp l, const std::wstring& replacement) 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)
83 { 109 {
84 return ReplaceMulti(workingString, templ, [&replacement]()->std::wstring 110 return ReplaceMulti(workingString, templ, [&replacement]()->std::wstring
85 { 111 {
86 return replacement; 112 return replacement;
87 }); 113 });
88 } 114 }
89 } 115 }
90 116
91 NotificationWindow::NotificationWindow(const AdblockPlus::Notification& notifica tion, const std::wstring& htmlFileDir) 117 NotificationWindow::NotificationWindow(const AdblockPlus::Notification& notifica tion, const std::wstring& htmlFileDir)
92 : m_dpi(96) 118 {
93 { 119 const std::wstring filePath = htmlFileDir + L"NotificationWindow.html";
94 m_htmlPage = [](const std::wstring& filePath)->std::wstring 120 std::wifstream ifs(filePath);
95 { 121 assert(ifs.good() && "Cannot open NotificationWindow.html file");
96 std::wifstream ifs(filePath); 122 if (!ifs.good())
97 assert(ifs.good() && "Cannot open NotificationWindow.html file"); 123 {
98 if (!ifs.good()) 124 throw std::runtime_error("Cannot read NotificationWindow.html");
99 { 125 }
100 throw std::runtime_error("Cannot read NotificationWindow.html"); 126 m_htmlPage.assign((std::istreambuf_iterator<wchar_t>(ifs)), std::istreambuf_it erator<wchar_t>());
101 }
102 std::wstring fileContent;
103 fileContent.assign((std::istreambuf_iterator<wchar_t>(ifs)), std::istreambuf _iterator<wchar_t>());
104 return fileContent;
105 }(htmlFileDir + L"\\NotificationWindow.html");
106 127
107 m_links = ToUtf16Strings(notification.GetLinks()); 128 m_links = ToUtf16Strings(notification.GetLinks());
108 auto body = ToUtf16String(notification.GetMessageString()); 129 auto body = ToUtf16String(notification.GetMessageString());
109 uint32_t linkIDCounter = 0; 130 uint32_t linkIDCounter = 0;
110 body = ReplaceMulti(body, L"<a>", [this, &linkIDCounter]()->std::wstring 131 body = ReplaceMulti(body, L"<a>", [this, &linkIDCounter]()->std::wstring
111 { 132 {
112 return L"<a href=\"#\" data-linkID=\"" + std::to_wstring(linkIDCounter++) + L"\">"; 133 return L"<a href=\"#\" data-linkID=\"" + std::to_wstring(linkIDCounter++) + L"\">";
113 }); 134 });
114 assert(linkIDCounter == m_links.size() && "The amount of links in the text is different from the amount of provided links"); 135 assert(linkIDCounter == m_links.size() && "The amount of links in the text is different from the amount of provided links");
115 m_htmlPage = ReplaceMulti(m_htmlPage, L"<!--Title-->", ToUtf16String(notificat ion.GetTitle())); 136 m_htmlPage = ReplaceMulti(m_htmlPage, L"<!--Title-->", ToUtf16String(notificat ion.GetTitle()));
116 m_htmlPage = ReplaceMulti(m_htmlPage, L"<!--Body-->", body); 137 m_htmlPage = ReplaceMulti(m_htmlPage, L"<!--Body-->", body);
117 } 138 }
118 139
119 NotificationWindow::~NotificationWindow() 140 NotificationWindow::~NotificationWindow()
120 { 141 {
121 } 142 }
122 143
123 LRESULT NotificationWindow::OnCreate(const CREATESTRUCT* /*createStruct*/) { 144 LRESULT NotificationWindow::OnCreate(const CREATESTRUCT* /*createStruct*/) {
145 {
146 DCHandle hdc(GetDC());
147 m_dpi = hdc.GetDeviceCaps(LOGPIXELSX);
148 }
124 m_bgColor.CreateSolidBrush(RGB(255, 255, 255)); 149 m_bgColor.CreateSolidBrush(RGB(255, 255, 255));
125 auto windowCoords = GetWindowCoordinates(); 150
126 MoveWindow(windowCoords.x, windowCoords.y, DPIAware(kWindowWidth), DPIAware(kW indowHeight)); 151 CRect iconRect(CPoint(0, 0), CSize(kIconSize + 2 * kIconPadding, kIconSize + 2 * kIconPadding));
127 152 m_icon.Create(m_hWnd, DPIAware(iconRect), nullptr, WS_CHILD | WS_VISIBLE | SS_ BITMAP | SS_CENTERIMAGE);
128 m_icon.Create(m_hWnd,
129 DPIAware(CRect(CPoint(0, 0), CSize(kIconSize, kIconSize))),
130 nullptr, WS_CHILD | WS_VISIBLE | SS_BITMAP | SS_CENTERIMAGE);
131 LoadABPIcon(); 153 LoadABPIcon();
132 154
133 m_axIE.Create(m_hWnd, DPIAware(CRect(CPoint(kIconSize, 0), CSize(kWindowWidth - kIconSize, kWindowHeight))), 155 m_axIE.Create(m_hWnd, DPIAware(CRect(CPoint(iconRect.right, 0), CSize(kWindowW idth - iconRect.right, kWindowHeight))),
134 L"", WS_CHILD | WS_VISIBLE, 0, kHTMLDocumentCtrlID); 156 L"", WS_CHILD | WS_VISIBLE, 0, kHTMLDocumentCtrlID);
135 m_axIE.CreateControl((L"mshtml:" + m_htmlPage).c_str()); 157 m_axIE.CreateControl((L"mshtml:" + m_htmlPage).c_str());
136 ATL::CComPtr<IAxWinAmbientDispatch> axWinAmbient; 158 ATL::CComPtr<IAxWinAmbientDispatch> axWinAmbient;
137 if (SUCCEEDED(m_axIE.QueryHost(&axWinAmbient))) { 159 if (SUCCEEDED(m_axIE.QueryHost(&axWinAmbient))) {
138 // disable web browser context menu 160 // disable web browser context menu
139 axWinAmbient->put_AllowContextMenu(VARIANT_FALSE); 161 axWinAmbient->put_AllowContextMenu(VARIANT_FALSE);
140 // make web browser DPI aware, so the browser itself sets zoom level and 162 // make web browser DPI aware, so the browser itself sets zoom level and
141 // cares about rendering (not zooming) in the proper size. 163 // cares about rendering (not zooming) in the proper size.
142 DWORD docFlags; 164 DWORD docFlags;
143 axWinAmbient->get_DocHostFlags(&docFlags); 165 axWinAmbient->get_DocHostFlags(&docFlags);
(...skipping 13 matching lines...) Expand all
157 { 179 {
158 if (reinterpret_cast<HWND>(lParam) != m_icon) 180 if (reinterpret_cast<HWND>(lParam) != m_icon)
159 { 181 {
160 handled = FALSE; 182 handled = FALSE;
161 } 183 }
162 return reinterpret_cast<LRESULT>(static_cast<HBRUSH>(m_bgColor)); 184 return reinterpret_cast<LRESULT>(static_cast<HBRUSH>(m_bgColor));
163 } 185 }
164 186
165 LRESULT NotificationWindow::OnClick(UINT /*msg*/, WPARAM /*wParam*/, LPARAM /*lP aram*/, BOOL& /*handled*/) 187 LRESULT NotificationWindow::OnClick(UINT /*msg*/, WPARAM /*wParam*/, LPARAM /*lP aram*/, BOOL& /*handled*/)
166 { 188 {
167 DestroyWindow(); 189 if(m_onClickCallback)
190 m_onClickCallback();
168 return 0; 191 return 0;
169 } 192 }
170 193
171 void NotificationWindow::OnSize(uint32_t wParam, CSize size) 194 void NotificationWindow::OnSize(uint32_t wParam, CSize size)
172 { 195 {
196 if (m_icon)
197 {
198 CRect rect(CPoint(0, 0), DPIAware(CSize(kIconSize + 2 * kIconPadding, kIconS ize + 2 * kIconPadding)));
199 m_icon.SetWindowPos(0, &rect, SWP_NOMOVE);
200 }
173 if (m_axIE) 201 if (m_axIE)
174 { 202 {
175 size.cx -= kIconSize; 203 size.cx -= DPIAware(kIconSize + 2 * kIconPadding);
176 CRect rect(CPoint(0, 0), size); 204 CRect rect(CPoint(0, 0), size);
177 m_axIE.SetWindowPos(0, &rect, SWP_NOMOVE); 205 m_axIE.SetWindowPos(0, &rect, SWP_NOMOVE);
178 } 206 }
179 SetMsgHandled(false); 207 SetMsgHandled(false);
180 } 208 }
181 209
182 void NotificationWindow::OnDestroy() 210 void NotificationWindow::OnDestroy()
183 { 211 {
184 AtlAdviseSinkMap(this, false); 212 AtlAdviseSinkMap(this, false);
185 // and proceed as usual 213 // and proceed as usual
(...skipping 13 matching lines...) Expand all
199 } 227 }
200 ATL::CComBSTR tag; 228 ATL::CComBSTR tag;
201 htmlElement->get_tagName(&tag); 229 htmlElement->get_tagName(&tag);
202 const wchar_t expectedTag[] = { L"a" }; 230 const wchar_t expectedTag[] = { L"a" };
203 if (_wcsnicmp(tag, expectedTag, min(sizeof(expectedTag), tag.Length())) != 0) { 231 if (_wcsnicmp(tag, expectedTag, min(sizeof(expectedTag), tag.Length())) != 0) {
204 return; 232 return;
205 } 233 }
206 auto classAttr = GetHtmlElementAttribute(*htmlElement, ATL::CComBSTR(L"class") ); 234 auto classAttr = GetHtmlElementAttribute(*htmlElement, ATL::CComBSTR(L"class") );
207 if (classAttr.attributeValue == L"closeButton") 235 if (classAttr.attributeValue == L"closeButton")
208 { 236 {
209 PostMessage(WM_CLOSE); 237 if (m_onCloseCallback)
210 return; 238 m_onCloseCallback();
211 } 239 return;
212 if (!linkClicked) 240 }
241 if (!m_onLinkClickedCallback)
213 { 242 {
214 return; 243 return;
215 } 244 }
216 auto linkIDAttr = GetHtmlElementAttribute(*htmlElement, ATL::CComBSTR(L"data-l inkID")); 245 auto linkIDAttr = GetHtmlElementAttribute(*htmlElement, ATL::CComBSTR(L"data-l inkID"));
217 uint32_t linkID = 0; 246 uint32_t linkID = 0;
218 if (!linkIDAttr.attributeValue.empty() && (linkID = std::stoi(linkIDAttr.attri buteValue)) < m_links.size()) 247 if (!linkIDAttr.attributeValue.empty() && (linkID = std::stoi(linkIDAttr.attri buteValue)) < m_links.size())
219 { 248 {
220 linkClicked(m_links[linkID]); 249 m_onLinkClickedCallback(m_links[linkID]);
250 if (m_onCloseCallback)
251 m_onCloseCallback();
221 } 252 }
222 } 253 }
223 254
224 void __stdcall NotificationWindow::OnHTMLDocumentSelectStart(IHTMLEventObj* even tObject) 255 void __stdcall NotificationWindow::OnHTMLDocumentSelectStart(IHTMLEventObj* even tObject)
225 { 256 {
226 if (!eventObject) 257 if (!eventObject)
227 return; 258 return;
228 // disable selecting 259 // disable selecting
229 eventObject->put_cancelBubble(VARIANT_TRUE); 260 eventObject->put_cancelBubble(VARIANT_TRUE);
230 eventObject->put_returnValue(ATL::CComVariant(false)); 261 eventObject->put_returnValue(ATL::CComVariant(false));
231 }
232
233 void NotificationWindow::OnFinalMessage(HWND) {
234 if (!!destroyed) {
235 destroyed();
236 }
237 } 262 }
238 263
239 void NotificationWindow::LoadABPIcon() 264 void NotificationWindow::LoadABPIcon()
240 { 265 {
241 ScopedModule m_adblockPlusDLL; 266 ScopedModule m_adblockPlusDLL;
242 if (!(m_adblockPlusDLL.Open(L"AdblockPlus32.dll", LOAD_LIBRARY_AS_DATAFILE) || 267 if (!(m_adblockPlusDLL.Open(L"AdblockPlus32.dll", LOAD_LIBRARY_AS_DATAFILE) ||
243 m_adblockPlusDLL.Open(L"AdblockPlus64.dll", LOAD_LIBRARY_AS_DATAFILE) || 268 m_adblockPlusDLL.Open(L"AdblockPlus64.dll", LOAD_LIBRARY_AS_DATAFILE) ||
244 // for debug 269 // for debug
245 m_adblockPlusDLL.Open(L"AdblockPlus.dll", LOAD_LIBRARY_AS_DATAFILE))) 270 m_adblockPlusDLL.Open(L"AdblockPlus.dll", LOAD_LIBRARY_AS_DATAFILE)))
246 { 271 {
(...skipping 10 matching lines...) Expand all
257 if (hIcon == nullptr) 282 if (hIcon == nullptr)
258 { 283 {
259 return; 284 return;
260 } 285 }
261 286
262 HDC screenDC = m_icon.GetDC(); 287 HDC screenDC = m_icon.GetDC();
263 DCHandle tmpDC(::CreateCompatibleDC(screenDC)); 288 DCHandle tmpDC(::CreateCompatibleDC(screenDC));
264 ScopedObjectHandle<HBITMAP> bitmap; 289 ScopedObjectHandle<HBITMAP> bitmap;
265 bitmap = CreateCompatibleBitmap(screenDC, DPIAware(kIconSize), DPIAware(kIconS ize)); 290 bitmap = CreateCompatibleBitmap(screenDC, DPIAware(kIconSize), DPIAware(kIconS ize));
266 HBITMAP prevBitmap = tmpDC.SelectBitmap(bitmap); 291 HBITMAP prevBitmap = tmpDC.SelectBitmap(bitmap);
267 RECT tmpRect = {0, 0, DPIAware(kIconSize), DPIAware(kIconSize)}; 292 CRect tmpRect(CPoint(0, 0), DPIAware(CSize(kIconSize, kIconSize)));
268 FillRect(tmpDC, &tmpRect, m_bgColor); 293 FillRect(tmpDC, &tmpRect, m_bgColor);
269 ::DrawIconEx(tmpDC, 0, 0, hIcon, DPIAware(kIconSize), DPIAware(kIconSize), 0, nullptr, DI_NORMAL); 294 ::DrawIconEx(tmpDC, 0, 0, hIcon, tmpRect.Width(), tmpRect.Height(), 0, nullptr , DI_NORMAL);
270 m_iconImg.Attach(bitmap.Detach()); 295 m_iconImg.Attach(bitmap.Detach());
271 tmpDC.SelectBitmap(prevBitmap); 296 tmpDC.SelectBitmap(prevBitmap);
272 m_icon.SetBitmap(m_iconImg); 297 m_icon.SetBitmap(m_iconImg);
273 } 298 }
274 299
275 POINT NotificationWindow::GetWindowCoordinates() { 300 POINT NotificationBorderWindow::GetWindowCoordinates() {
276 HMONITOR primaryMonitor = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTOPRIMARY); 301 HMONITOR primaryMonitor = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTOPRIMARY);
277 { 302 {
278 DCHandle hdc(GetDC()); 303 DCHandle hdc(GetDC());
279 m_dpi = hdc.GetDeviceCaps(LOGPIXELSX); 304 m_dpi = hdc.GetDeviceCaps(LOGPIXELSX);
Oleksandr 2015/08/18 11:47:40 Is this still needed, seeing that we now get dpi i
sergei 2015/08/18 11:58:40 It's a little bit odd. `GetWindowCoordinates` is a
280 } 305 }
281 MONITORINFO monitorInfo = {}; 306 MONITORINFO monitorInfo = {};
282 monitorInfo.cbSize = sizeof(monitorInfo); 307 monitorInfo.cbSize = sizeof(monitorInfo);
283 GetMonitorInfo(primaryMonitor, &monitorInfo); 308 GetMonitorInfo(primaryMonitor, &monitorInfo);
284 int windowX = monitorInfo.rcWork.right - DPIAware(kWindowWidth + kWindowMargin Right); 309 int windowX = monitorInfo.rcWork.right - DPIAware(kWindowWidth + kWindowMargin Right);
285 int windowY = monitorInfo.rcWork.bottom - DPIAware(kWindowHeight + kWindowMarg inBottom); 310 int windowY = monitorInfo.rcWork.bottom - DPIAware(kWindowHeight + kWindowMarg inBottom);
286 POINT coords = {windowX, windowY}; 311 POINT coords = {windowX, windowY};
287 return coords; 312 return coords;
313 }
314
315 NotificationBorderWindow::NotificationBorderWindow(const AdblockPlus::Notificati on& notification, const std::wstring& htmlFileDir)
316 : m_content(notification, htmlFileDir)
317 {
318 m_content.SetOnClick([this]
319 {
320 PostMessage(WM_CLOSE);
321 });
322 m_content.SetOnClose([this]
323 {
324 PostMessage(WM_CLOSE);
325 });
326 }
327
328 LRESULT NotificationBorderWindow::OnCreate(const CREATESTRUCT* createStruct)
329 {
330 auto windowCoords = GetWindowCoordinates();
331 MoveWindow(windowCoords.x, windowCoords.y, DPIAware(kWindowWidth), DPIAware(kW indowHeight));
332
333 RECT clientRect;
334 GetClientRect(&clientRect);
335 // make one pixel border
336 clientRect.top += 1;
337 clientRect.left += 1;
338 clientRect.bottom -= 1;
339 clientRect.right -= 1;
340 m_content.Create(m_hWnd, clientRect, nullptr, WS_CHILD | WS_VISIBLE);
341 auto err = GetLastError();
342 SetMsgHandled(false);
343 return 0;
344 }
345
346 void NotificationBorderWindow::OnSize(uint32_t wParam, CSize size)
347 {
348 if (m_content.IsWindow())
349 {
350 RECT clientRect;
351 GetClientRect(&clientRect);
352 clientRect.top += 1;
353 clientRect.left += 1;
354 clientRect.bottom -= 1;
355 clientRect.right -= 1;
356 m_content.MoveWindow(&clientRect);
357 }
358 SetMsgHandled(false);
359 }
360
361 LRESULT NotificationBorderWindow::OnClick(UINT /*msg*/, WPARAM /*wParam*/, LPARA M /*lParam*/, BOOL& /*handled*/)
362 {
363 DestroyWindow();
364 return 0;
365 }
366
367 void NotificationBorderWindow::OnFinalMessage(HWND) {
368 if (!!m_onDestroyedCallback) {
369 m_onDestroyedCallback();
370 }
288 } 371 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld