summaryrefslogtreecommitdiffstats
path: root/widget/windows/ToastNotificationHeaderOnlyUtils.h
blob: 533d5bec0043ffd19184d0b4171796f1bab3d48b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef mozilla_ToastNotificationHeaderOnlyUtils_h
#define mozilla_ToastNotificationHeaderOnlyUtils_h

/**
 * This header is intended for self-contained, header-only, utility code to
 * share between Windows toast notification code in firefox.exe and
 * notificationserver.dll.
 */

// Use XPCOM logging if we're in a XUL context, otherwise use Windows Event
// logging.
// NOTE: The `printf` `format` equivalent argument to `NOTIFY_LOG` is converted
// to a wide string when outside of a XUL context. String format specifiers need
// to specify they're a wide string with `%ls` or narrow string with `%hs`.
#include "mozilla/Logging.h"
#ifdef IMPL_LIBXUL
namespace mozilla::widget {
extern LazyLogModule sWASLog;
}  // namespace mozilla::widget
#  define NOTIFY_LOG(_level, _args) \
    MOZ_LOG(mozilla::widget::sWASLog, _level, _args)
#else
#  include "mozilla/WindowsEventLog.h"

bool gVerbose = false;

#  define NOTIFY_LOG(_level, _args)                       \
    if (gVerbose || _level == mozilla::LogLevel::Error) { \
      POST_EXPAND_NOTIFY_LOG(MOZ_LOG_EXPAND_ARGS _args);  \
    }
#  define POST_EXPAND_NOTIFY_LOG(...) \
    MOZ_WIN_EVENT_LOG_ERROR_MESSAGE(  \
        L"" MOZ_APP_DISPLAYNAME " Notification Server", L"" __VA_ARGS__)
#endif

#include <functional>
#include <string>

#include "nsWindowsHelpers.h"

namespace mozilla::widget::toastnotification {

const wchar_t kLaunchArgProgram[] = L"program";
const wchar_t kLaunchArgProfile[] = L"profile";
const wchar_t kLaunchArgUrl[] = L"launchUrl";
const wchar_t kLaunchArgPrivilegedName[] = L"privilegedName";
const wchar_t kLaunchArgTag[] = L"windowsTag";
const wchar_t kLaunchArgLogging[] = L"logging";
const wchar_t kLaunchArgAction[] = L"action";

const DWORD kNotificationServerTimeoutMs = (10 * 1000);

struct ToastNotificationPidMessage {
  DWORD pid = 0;
};

struct ToastNotificationPermissionMessage {
  DWORD setForegroundPermissionGranted = 0;
};

inline std::wstring GetNotificationPipeName(const wchar_t* aTag) {
  // Prefix required by pipe API.
  std::wstring pipeName(LR"(\\.\pipe\)");

  pipeName += L"" MOZ_APP_NAME;
  pipeName += aTag;

  return pipeName;
}

inline bool WaitEventWithTimeout(const HANDLE& event) {
  DWORD result = WaitForSingleObject(event, kNotificationServerTimeoutMs);

  switch (result) {
    case WAIT_OBJECT_0:
      NOTIFY_LOG(LogLevel::Info, ("Pipe wait signaled"));
      return true;
    case WAIT_TIMEOUT:
      NOTIFY_LOG(LogLevel::Warning, ("Pipe wait timed out"));
      return false;
    case WAIT_FAILED:
      NOTIFY_LOG(LogLevel::Error,
                 ("Pipe wait failed, error %lu", GetLastError()));
      return false;
    case WAIT_ABANDONED:
      NOTIFY_LOG(LogLevel::Error, ("Pipe wait abandoned"));
      return false;
    default:
      NOTIFY_LOG(LogLevel::Error, ("Pipe wait unknown error"));
      return false;
  }
}

/* Handles running overlapped transactions for a Windows pipe. This function
 * manages lifetimes of Event and OVERLAPPED objects to ensure they are not used
 * while an overlapped operation is pending. */
inline bool SyncDoOverlappedIOWithTimeout(
    const nsAutoHandle& pipe, const size_t bytesExpected,
    const std::function<BOOL(OVERLAPPED&)>& transactPipe) {
  nsAutoHandle event(CreateEventW(nullptr, TRUE, FALSE, nullptr));
  if (!event) {
    NOTIFY_LOG(
        LogLevel::Error,
        ("Error creating pipe transaction event, error %lu", GetLastError()));
    return false;
  }

  OVERLAPPED overlapped{};
  overlapped.hEvent = event.get();
  BOOL result = transactPipe(overlapped);

  if (!result && GetLastError() != ERROR_IO_PENDING) {
    NOTIFY_LOG(LogLevel::Error,
               ("Error reading from pipe, error %lu", GetLastError()));
    return false;
  }

  if (!WaitEventWithTimeout(overlapped.hEvent)) {
    NOTIFY_LOG(LogLevel::Warning, ("Pipe transaction timed out, canceling "
                                   "(transaction may still succeed)."));

    CancelIo(pipe.get());

    // Transaction may still succeed before cancellation is handled; fall
    // through to normal handling.
  }

  DWORD bytesTransferred = 0;
  // Pipe transfer has either been signaled or cancelled by this point, so it
  // should be safe to wait on.
  BOOL overlappedResult =
      GetOverlappedResult(pipe.get(), &overlapped, &bytesTransferred, TRUE);

  if (!overlappedResult) {
    NOTIFY_LOG(
        LogLevel::Error,
        ("Error retrieving pipe overlapped result, error %lu", GetLastError()));
    return false;
  } else if (bytesTransferred != bytesExpected) {
    NOTIFY_LOG(LogLevel::Error,
               ("%lu bytes read from pipe, but %zu bytes expected",
                bytesTransferred, bytesExpected));
    return false;
  }

  return true;
}

}  // namespace mozilla::widget::toastnotification

#endif  // mozilla_ToastNotificationHeaderOnlyUtils_h