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