зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 3 changesets (bug 1883476, bug 1826375) for causing windows build bustages. CLOSED TREE
Backed out changeset bc8bdcfbcd9c (bug 1883476) Backed out changeset 7d6333da6d31 (bug 1826375) Backed out changeset f5f32253c79c (bug 1826375)
This commit is contained in:
Родитель
f2129be556
Коммит
bde44fb4ce
|
@ -295,6 +295,9 @@ pref("browser.shell.checkDefaultPDF", true);
|
|||
// Will be set to `true` if the user indicates that they don't want to be asked
|
||||
// again about Firefox being their default PDF handler any more.
|
||||
pref("browser.shell.checkDefaultPDF.silencedByUser", false);
|
||||
// URL to navigate to when launching Firefox after accepting the Windows Default
|
||||
// Browser Agent "Set Firefox as default" call to action.
|
||||
pref("browser.shell.defaultBrowserAgent.thanksURL", "https://www.mozilla.org/%LOCALE%/firefox/set-as-default/thanks/");
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -1457,6 +1457,29 @@ nsDefaultCommandLineHandler.prototype = {
|
|||
console.error(e);
|
||||
}
|
||||
|
||||
if (
|
||||
AppConstants.platform == "win" &&
|
||||
cmdLine.handleFlag("to-handle-default-browser-agent", false)
|
||||
) {
|
||||
// The Default Browser Agent launches Firefox in response to a Windows
|
||||
// native notification, but it does so in a non-standard manner.
|
||||
Services.telemetry.setEventRecordingEnabled(
|
||||
"browser.launched_to_handle",
|
||||
true
|
||||
);
|
||||
Glean.browserLaunchedToHandle.systemNotification.record({
|
||||
name: "default-browser-agent",
|
||||
});
|
||||
|
||||
let thanksURI = Services.io.newURI(
|
||||
Services.urlFormatter.formatURLPref(
|
||||
"browser.shell.defaultBrowserAgent.thanksURL"
|
||||
)
|
||||
);
|
||||
urilist.push(thanksURI);
|
||||
principalList.push(lazy.gSystemPrincipal);
|
||||
}
|
||||
|
||||
if (cmdLine.findFlag("screenshot", true) != -1) {
|
||||
// Shouldn't have to push principal here with the screenshot flag
|
||||
lazy.HeadlessShell.handleCmdLineArgs(
|
||||
|
|
|
@ -33,5 +33,8 @@ skip-if = ["os == 'mac'"]
|
|||
|
||||
["browser_startup_homepage.js"]
|
||||
|
||||
["browser_system_notification_telemetry.js"]
|
||||
run-if = ["os == 'win'"]
|
||||
|
||||
["browser_to_handle_telemetry.js"]
|
||||
run-if = ["os == 'win'"]
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
async function handleCommandLine(args, state) {
|
||||
let newWinPromise;
|
||||
let target = Services.urlFormatter.formatURLPref(
|
||||
"browser.shell.defaultBrowserAgent.thanksURL"
|
||||
);
|
||||
|
||||
const EXISTING_FILE = Cc["@mozilla.org/file/local;1"].createInstance(
|
||||
Ci.nsIFile
|
||||
);
|
||||
EXISTING_FILE.initWithPath(getTestFilePath("dummy.pdf"));
|
||||
|
||||
if (state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
|
||||
newWinPromise = BrowserTestUtils.waitForNewWindow({
|
||||
url: target, // N.b.: trailing slashes matter when matching.
|
||||
});
|
||||
}
|
||||
|
||||
let cmdLineHandler = Cc["@mozilla.org/browser/final-clh;1"].getService(
|
||||
Ci.nsICommandLineHandler
|
||||
);
|
||||
|
||||
let fakeCmdLine = Cu.createCommandLine(args, EXISTING_FILE.parent, state);
|
||||
cmdLineHandler.handle(fakeCmdLine);
|
||||
|
||||
if (newWinPromise) {
|
||||
let newWin = await newWinPromise;
|
||||
await BrowserTestUtils.closeWindow(newWin);
|
||||
} else {
|
||||
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
}
|
||||
}
|
||||
|
||||
// Launching from the WDBA should open the "thanks" page and should send a
|
||||
// telemetry event.
|
||||
add_task(async function test_launched_to_handle_default_browser_agent() {
|
||||
await handleCommandLine(
|
||||
["-to-handle-default-browser-agent"],
|
||||
Ci.nsICommandLine.STATE_INITIAL_LAUNCH
|
||||
);
|
||||
|
||||
TelemetryTestUtils.assertEvents(
|
||||
[{ extra: { name: "default-browser-agent" } }],
|
||||
{
|
||||
category: "browser.launched_to_handle",
|
||||
method: "system_notification",
|
||||
object: "toast",
|
||||
}
|
||||
);
|
||||
});
|
|
@ -32,7 +32,7 @@ ac_add_options --enable-disk-remnant-avoidance
|
|||
ac_add_options --disable-webrtc # Bug 1393901
|
||||
ac_add_options --disable-geckodriver # Bug 1489320
|
||||
ac_add_options --disable-update-agent # Bug 1561797
|
||||
ac_add_options --disable-default-browser-agent # Relies on toast notifications which don't build on mingw.
|
||||
ac_add_options --disable-default-browser-agent # WinToast does not build on mingw
|
||||
ac_add_options --disable-notification-server # Toast notifications don't build on mingw.
|
||||
|
||||
# Find our toolchain
|
||||
|
|
|
@ -32,7 +32,7 @@ ac_add_options --enable-disk-remnant-avoidance
|
|||
ac_add_options --disable-webrtc # Bug 1393901
|
||||
ac_add_options --disable-geckodriver # Bug 1489320
|
||||
ac_add_options --disable-update-agent # Bug 1561797
|
||||
ac_add_options --disable-default-browser-agent # Relies on toast notifications which don't build on mingw.
|
||||
ac_add_options --disable-default-browser-agent # WinToast does not build on mingw
|
||||
ac_add_options --disable-notification-server # Toast notifications don't build on mingw.
|
||||
|
||||
# Find our toolchain
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2016 Mohammed Boujemaoui Boulaghmoudi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,81 @@
|
|||
diff --git a/src/wintoastlib.cpp b/src/wintoastlib.cpp
|
||||
index 0895ff7..ac8d5cf 100644
|
||||
--- a/src/wintoastlib.cpp
|
||||
+++ b/src/wintoastlib.cpp
|
||||
@@ -213,8 +213,8 @@ namespace Util {
|
||||
}
|
||||
|
||||
|
||||
- inline HRESULT defaultShellLinksDirectory(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) {
|
||||
- DWORD written = GetEnvironmentVariableW(L"APPDATA", path, nSize);
|
||||
+ inline HRESULT commonShellLinksDirectory(_In_ const WCHAR* baseEnv, _In_ WCHAR* path, _In_ DWORD nSize) {
|
||||
+ DWORD written = GetEnvironmentVariableW(baseEnv, path, nSize);
|
||||
HRESULT hr = written > 0 ? S_OK : E_INVALIDARG;
|
||||
if (SUCCEEDED(hr)) {
|
||||
errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH);
|
||||
@@ -224,8 +224,8 @@ namespace Util {
|
||||
return hr;
|
||||
}
|
||||
|
||||
- inline HRESULT defaultShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) {
|
||||
- HRESULT hr = defaultShellLinksDirectory(path, nSize);
|
||||
+ inline HRESULT commonShellLinkPath(_In_ const WCHAR* baseEnv, const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize) {
|
||||
+ HRESULT hr = commonShellLinksDirectory(baseEnv, path, nSize);
|
||||
if (SUCCEEDED(hr)) {
|
||||
const std::wstring appLink(appname + DEFAULT_LINK_FORMAT);
|
||||
errno_t result = wcscat_s(path, nSize, appLink.c_str());
|
||||
@@ -235,6 +235,13 @@ namespace Util {
|
||||
return hr;
|
||||
}
|
||||
|
||||
+ inline HRESULT defaultUserShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) {
|
||||
+ return commonShellLinkPath(L"APPDATA", appname, path, nSize);
|
||||
+ }
|
||||
+
|
||||
+ inline HRESULT defaultSystemShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) {
|
||||
+ return commonShellLinkPath(L"PROGRAMDATA", appname, path, nSize);
|
||||
+ }
|
||||
|
||||
inline PCWSTR AsString(ComPtr<IXmlDocument> &xmlDocument) {
|
||||
HSTRING xml;
|
||||
@@ -523,12 +530,19 @@ const std::wstring& WinToast::appUserModelId() const {
|
||||
|
||||
HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) {
|
||||
WCHAR path[MAX_PATH] = { L'\0' };
|
||||
- Util::defaultShellLinkPath(_appName, path);
|
||||
+ Util::defaultUserShellLinkPath(_appName, path);
|
||||
// Check if the file exist
|
||||
DWORD attr = GetFileAttributesW(path);
|
||||
if (attr >= 0xFFFFFFF) {
|
||||
- DEBUG_MSG("Error, shell link not found. Try to create a new one in: " << path);
|
||||
- return E_FAIL;
|
||||
+ // The shortcut may be in the system Start Menu.
|
||||
+ WCHAR systemPath[MAX_PATH] = { L'\0' };
|
||||
+ Util::defaultSystemShellLinkPath(_appName, systemPath);
|
||||
+ attr = GetFileAttributesW(systemPath);
|
||||
+ if (attr >= 0xFFFFFFF) {
|
||||
+ DEBUG_MSG("Error, shell link not found. Try to create a new one in: " << path);
|
||||
+ return E_FAIL;
|
||||
+ }
|
||||
+ wcscpy(path, systemPath);
|
||||
}
|
||||
|
||||
// Let's load the file as shell link to validate.
|
||||
@@ -543,7 +557,7 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) {
|
||||
ComPtr<IPersistFile> persistFile;
|
||||
hr = shellLink.As(&persistFile);
|
||||
if (SUCCEEDED(hr)) {
|
||||
- hr = persistFile->Load(path, STGM_READWRITE);
|
||||
+ hr = persistFile->Load(path, STGM_READ);
|
||||
if (SUCCEEDED(hr)) {
|
||||
ComPtr<IPropertyStore> propertyStore;
|
||||
hr = shellLink.As(&propertyStore);
|
||||
@@ -583,7 +597,7 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) {
|
||||
HRESULT WinToast::createShellLinkHelper() {
|
||||
WCHAR exePath[MAX_PATH]{L'\0'};
|
||||
WCHAR slPath[MAX_PATH]{L'\0'};
|
||||
- Util::defaultShellLinkPath(_appName, slPath);
|
||||
+ Util::defaultUserShellLinkPath(_appName, slPath);
|
||||
Util::defaultExecutablePath(exePath);
|
||||
ComPtr<IShellLinkW> shellLink;
|
||||
HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
|
|
@ -0,0 +1,110 @@
|
|||
diff --git a/src/wintoastlib.cpp b/src/wintoastlib.cpp
|
||||
index 0895ff7..52de554 100644
|
||||
--- a/src/wintoastlib.cpp
|
||||
+++ b/src/wintoastlib.cpp
|
||||
@@ -391,6 +391,10 @@ void WinToast::setAppUserModelId(_In_ const std::wstring& aumi) {
|
||||
DEBUG_MSG(L"Default App User Model Id: " << _aumi.c_str());
|
||||
}
|
||||
|
||||
+void WinToast::setShortcutPolicy(_In_ ShortcutPolicy shortcutPolicy) {
|
||||
+ _shortcutPolicy = shortcutPolicy;
|
||||
+}
|
||||
+
|
||||
bool WinToast::isCompatible() {
|
||||
DllImporter::initialize();
|
||||
return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr)
|
||||
@@ -492,10 +496,12 @@ bool WinToast::initialize(_Out_ WinToastError* error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
- if (createShortcut() < 0) {
|
||||
- setError(error, WinToastError::ShellLinkNotCreated);
|
||||
- DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =(");
|
||||
- return false;
|
||||
+ if (_shortcutPolicy != SHORTCUT_POLICY_IGNORE) {
|
||||
+ if (createShortcut() < 0) {
|
||||
+ setError(error, WinToastError::ShellLinkNotCreated);
|
||||
+ DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =(");
|
||||
+ return false;
|
||||
+ }
|
||||
}
|
||||
|
||||
if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID(_aumi.c_str()))) {
|
||||
@@ -555,18 +561,23 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) {
|
||||
hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH);
|
||||
wasChanged = false;
|
||||
if (FAILED(hr) || _aumi != AUMI) {
|
||||
- // AUMI Changed for the same app, let's update the current value! =)
|
||||
- wasChanged = true;
|
||||
- PropVariantClear(&appIdPropVar);
|
||||
- hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar);
|
||||
- if (SUCCEEDED(hr)) {
|
||||
- hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar);
|
||||
+ if (_shortcutPolicy == SHORTCUT_POLICY_REQUIRE_CREATE) {
|
||||
+ // AUMI Changed for the same app, let's update the current value! =)
|
||||
+ wasChanged = true;
|
||||
+ PropVariantClear(&appIdPropVar);
|
||||
+ hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar);
|
||||
if (SUCCEEDED(hr)) {
|
||||
- hr = propertyStore->Commit();
|
||||
- if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) {
|
||||
- hr = persistFile->Save(path, TRUE);
|
||||
+ hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar);
|
||||
+ if (SUCCEEDED(hr)) {
|
||||
+ hr = propertyStore->Commit();
|
||||
+ if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) {
|
||||
+ hr = persistFile->Save(path, TRUE);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
+ } else {
|
||||
+ // Not allowed to touch the shortcut to fix the AUMI
|
||||
+ hr = E_FAIL;
|
||||
}
|
||||
}
|
||||
PropVariantClear(&appIdPropVar);
|
||||
@@ -581,6 +592,10 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) {
|
||||
|
||||
|
||||
HRESULT WinToast::createShellLinkHelper() {
|
||||
+ if (_shortcutPolicy != SHORTCUT_POLICY_REQUIRE_CREATE) {
|
||||
+ return E_FAIL;
|
||||
+ }
|
||||
+
|
||||
WCHAR exePath[MAX_PATH]{L'\0'};
|
||||
WCHAR slPath[MAX_PATH]{L'\0'};
|
||||
Util::defaultShellLinkPath(_appName, slPath);
|
||||
diff --git a/src/wintoastlib.h b/src/wintoastlib.h
|
||||
index 68b1cb1..dc8d745 100644
|
||||
--- a/src/wintoastlib.h
|
||||
+++ b/src/wintoastlib.h
|
||||
@@ -173,6 +173,16 @@ namespace WinToastLib {
|
||||
SHORTCUT_CREATE_FAILED = -4
|
||||
};
|
||||
|
||||
+ enum ShortcutPolicy {
|
||||
+ /* Don't check, create, or modify a shortcut. */
|
||||
+ SHORTCUT_POLICY_IGNORE = 0,
|
||||
+ /* Require a shortcut with matching AUMI, don't create or modify an existing one. */
|
||||
+ SHORTCUT_POLICY_REQUIRE_NO_CREATE = 1,
|
||||
+ /* Require a shortcut with matching AUMI, create if missing, modify if not matching.
|
||||
+ * This is the default. */
|
||||
+ SHORTCUT_POLICY_REQUIRE_CREATE = 2,
|
||||
+ };
|
||||
+
|
||||
WinToast(void);
|
||||
virtual ~WinToast();
|
||||
static WinToast* instance();
|
||||
@@ -194,10 +204,12 @@ namespace WinToastLib {
|
||||
const std::wstring& appUserModelId() const;
|
||||
void setAppUserModelId(_In_ const std::wstring& aumi);
|
||||
void setAppName(_In_ const std::wstring& appName);
|
||||
+ void setShortcutPolicy(_In_ ShortcutPolicy policy);
|
||||
|
||||
protected:
|
||||
bool _isInitialized{false};
|
||||
bool _hasCoInitialized{false};
|
||||
+ ShortcutPolicy _shortcutPolicy{SHORTCUT_POLICY_REQUIRE_CREATE};
|
||||
std::wstring _appName{};
|
||||
std::wstring _aumi{};
|
||||
std::map<INT64, ComPtr<IToastNotification>> _buffer{};
|
|
@ -0,0 +1,37 @@
|
|||
# Version of this schema
|
||||
schema: 1
|
||||
|
||||
# Manual Update can be done with:
|
||||
# cd third_pary/WinToast
|
||||
# wget https://raw.githubusercontent.com/mohabouje/WinToast/master/src/wintoastlib.cpp
|
||||
# wget https://raw.githubusercontent.com/mohabouje/WinToast/master/src/wintoastlib.h
|
||||
# patch -p2 < moz-check-system-shortcut.patch
|
||||
# patch -p2 < moz-disable-create-shortcut.patch
|
||||
# patch -p2 < upstream-add-toast-scenario.patch
|
||||
|
||||
bugzilla:
|
||||
# Bugzilla product and component for this directory and subdirectories
|
||||
product: Toolkit
|
||||
component: "General"
|
||||
|
||||
# Document the source of externally hosted code
|
||||
origin:
|
||||
|
||||
# Short name of the package/library
|
||||
name: WinToast
|
||||
|
||||
description: WinToast is a lightly library written in C++ which brings a complete integration of the modern toast notifications of Windows 8 & Windows 10.
|
||||
|
||||
# Full URL for the package's homepage/etc
|
||||
# Usually different from repository url
|
||||
url: https://github.com/mohabouje/WinToast
|
||||
|
||||
# Human-readable identifier for this version/release
|
||||
# Generally "version NNN", "tag SSS", "bookmark SSS"
|
||||
release: commit 09227c72f16ccefc36e9d430dea3b435346dbcbc
|
||||
|
||||
# The package's license, where possible using the mnemonic from
|
||||
# https://spdx.org/licenses/
|
||||
# Multiple licenses can be specified (as a YAML list)
|
||||
# A "LICENSE" file must exist containing the full license text
|
||||
license: MIT
|
|
@ -0,0 +1,123 @@
|
|||
diff --git a/src/wintoastlib.cpp b/src/wintoastlib.cpp
|
||||
index 3cf5f21..1adfe19 100644
|
||||
--- a/src/wintoastlib.cpp
|
||||
+++ b/src/wintoastlib.cpp
|
||||
@@ -677,6 +677,10 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHan
|
||||
(toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long");
|
||||
}
|
||||
|
||||
+ if (SUCCEEDED(hr)) {
|
||||
+ hr = addScenarioHelper(xmlDocument.Get(), toast.scenario());
|
||||
+ }
|
||||
+
|
||||
} else {
|
||||
DEBUG_MSG("Modern features (Actions/Sounds/Attributes) not supported in this os version");
|
||||
}
|
||||
@@ -828,6 +832,28 @@ HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstr
|
||||
return hr;
|
||||
}
|
||||
|
||||
+HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ const std::wstring& scenario) {
|
||||
+ ComPtr<IXmlNodeList> nodeList;
|
||||
+ HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList);
|
||||
+ if (SUCCEEDED(hr)) {
|
||||
+ UINT32 length;
|
||||
+ hr = nodeList->get_Length(&length);
|
||||
+ if (SUCCEEDED(hr)) {
|
||||
+ ComPtr<IXmlNode> toastNode;
|
||||
+ hr = nodeList->Item(0, &toastNode);
|
||||
+ if (SUCCEEDED(hr)) {
|
||||
+ ComPtr<IXmlElement> toastElement;
|
||||
+ hr = toastNode.As(&toastElement);
|
||||
+ if (SUCCEEDED(hr)) {
|
||||
+ hr = toastElement->SetAttribute(WinToastStringWrapper(L"scenario").Get(),
|
||||
+ WinToastStringWrapper(scenario).Get());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return hr;
|
||||
+}
|
||||
+
|
||||
HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos) {
|
||||
ComPtr<IXmlNodeList> nodeList;
|
||||
HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList);
|
||||
@@ -1065,6 +1091,15 @@ void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) {
|
||||
_expiration = millisecondsFromNow;
|
||||
}
|
||||
|
||||
+void WinToastLib::WinToastTemplate::setScenario(Scenario scenario) {
|
||||
+ switch (scenario) {
|
||||
+ case Scenario::Default: _scenario = L"Default"; break;
|
||||
+ case Scenario::Alarm: _scenario = L"Alarm"; break;
|
||||
+ case Scenario::IncomingCall: _scenario = L"IncomingCall"; break;
|
||||
+ case Scenario::Reminder: _scenario = L"Reminder"; break;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
void WinToastTemplate::setAttributionText(_In_ const std::wstring& attributionText) {
|
||||
_attributionText = attributionText;
|
||||
}
|
||||
@@ -1112,6 +1147,10 @@ const std::wstring& WinToastTemplate::attributionText() const {
|
||||
return _attributionText;
|
||||
}
|
||||
|
||||
+const std::wstring& WinToastLib::WinToastTemplate::scenario() const {
|
||||
+ return _scenario;
|
||||
+}
|
||||
+
|
||||
INT64 WinToastTemplate::expiration() const {
|
||||
return _expiration;
|
||||
}
|
||||
diff --git a/src/wintoastlib.h b/src/wintoastlib.h
|
||||
index d028994..291e15f 100644
|
||||
--- a/src/wintoastlib.h
|
||||
+++ b/src/wintoastlib.h
|
||||
@@ -63,6 +63,7 @@ namespace WinToastLib {
|
||||
|
||||
class WinToastTemplate {
|
||||
public:
|
||||
+ enum class Scenario { Default, Alarm, IncomingCall, Reminder };
|
||||
enum Duration { System, Short, Long };
|
||||
enum AudioOption { Default = 0, Silent, Loop };
|
||||
enum TextField { FirstLine = 0, SecondLine, ThirdLine };
|
||||
@@ -114,13 +115,14 @@ namespace WinToastLib {
|
||||
void setSecondLine(_In_ const std::wstring& text);
|
||||
void setThirdLine(_In_ const std::wstring& text);
|
||||
void setTextField(_In_ const std::wstring& txt, _In_ TextField pos);
|
||||
- void setAttributionText(_In_ const std::wstring & attributionText);
|
||||
+ void setAttributionText(_In_ const std::wstring& attributionText);
|
||||
void setImagePath(_In_ const std::wstring& imgPath);
|
||||
void setAudioPath(_In_ WinToastTemplate::AudioSystemFile audio);
|
||||
void setAudioPath(_In_ const std::wstring& audioPath);
|
||||
void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption);
|
||||
void setDuration(_In_ Duration duration);
|
||||
void setExpiration(_In_ INT64 millisecondsFromNow);
|
||||
+ void setScenario(_In_ Scenario scenario);
|
||||
void addAction(_In_ const std::wstring& label);
|
||||
|
||||
std::size_t textFieldsCount() const;
|
||||
@@ -132,6 +134,7 @@ namespace WinToastLib {
|
||||
const std::wstring& imagePath() const;
|
||||
const std::wstring& audioPath() const;
|
||||
const std::wstring& attributionText() const;
|
||||
+ const std::wstring& scenario() const;
|
||||
INT64 expiration() const;
|
||||
WinToastTemplateType type() const;
|
||||
WinToastTemplate::AudioOption audioOption() const;
|
||||
@@ -142,6 +145,7 @@ namespace WinToastLib {
|
||||
std::wstring _imagePath{};
|
||||
std::wstring _audioPath{};
|
||||
std::wstring _attributionText{};
|
||||
+ std::wstring _scenario{L"Default"};
|
||||
INT64 _expiration{0};
|
||||
AudioOption _audioOption{WinToastTemplate::AudioOption::Default};
|
||||
WinToastTemplateType _type{WinToastTemplateType::Text01};
|
||||
@@ -210,6 +214,7 @@ namespace WinToastLib {
|
||||
HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text);
|
||||
HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& action, _In_ const std::wstring& arguments);
|
||||
HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration);
|
||||
+ HRESULT addScenarioHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& scenario);
|
||||
ComPtr<IToastNotifier> notifier(_In_ bool* succeded) const;
|
||||
void setError(_Out_opt_ WinToastError* error, _In_ WinToastError value);
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,234 @@
|
|||
/* * Copyright (C) 2016-2019 Mohammed Boujemaoui <mohabouje@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef WINTOASTLIB_H
|
||||
#define WINTOASTLIB_H
|
||||
#include <Windows.h>
|
||||
#include <sdkddkver.h>
|
||||
#include <WinUser.h>
|
||||
#include <ShObjIdl.h>
|
||||
#include <wrl/implements.h>
|
||||
#include <wrl/event.h>
|
||||
#include <windows.ui.notifications.h>
|
||||
#include <strsafe.h>
|
||||
#include <Psapi.h>
|
||||
#include <ShlObj.h>
|
||||
#include <roapi.h>
|
||||
#include <propvarutil.h>
|
||||
#include <functiondiscoverykeys.h>
|
||||
#include <iostream>
|
||||
#include <winstring.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace ABI::Windows::Data::Xml::Dom;
|
||||
using namespace ABI::Windows::Foundation;
|
||||
using namespace ABI::Windows::UI::Notifications;
|
||||
using namespace Windows::Foundation;
|
||||
|
||||
|
||||
namespace WinToastLib {
|
||||
|
||||
class IWinToastHandler {
|
||||
public:
|
||||
enum WinToastDismissalReason {
|
||||
UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled,
|
||||
ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden,
|
||||
TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut
|
||||
};
|
||||
virtual ~IWinToastHandler() = default;
|
||||
virtual void toastActivated() const = 0;
|
||||
virtual void toastActivated(int actionIndex) const = 0;
|
||||
virtual void toastDismissed(WinToastDismissalReason state) const = 0;
|
||||
virtual void toastFailed() const = 0;
|
||||
};
|
||||
|
||||
class WinToastTemplate {
|
||||
public:
|
||||
enum class Scenario { Default, Alarm, IncomingCall, Reminder };
|
||||
enum Duration { System, Short, Long };
|
||||
enum AudioOption { Default = 0, Silent, Loop };
|
||||
enum TextField { FirstLine = 0, SecondLine, ThirdLine };
|
||||
enum WinToastTemplateType {
|
||||
ImageAndText01 = ToastTemplateType::ToastTemplateType_ToastImageAndText01,
|
||||
ImageAndText02 = ToastTemplateType::ToastTemplateType_ToastImageAndText02,
|
||||
ImageAndText03 = ToastTemplateType::ToastTemplateType_ToastImageAndText03,
|
||||
ImageAndText04 = ToastTemplateType::ToastTemplateType_ToastImageAndText04,
|
||||
Text01 = ToastTemplateType::ToastTemplateType_ToastText01,
|
||||
Text02 = ToastTemplateType::ToastTemplateType_ToastText02,
|
||||
Text03 = ToastTemplateType::ToastTemplateType_ToastText03,
|
||||
Text04 = ToastTemplateType::ToastTemplateType_ToastText04,
|
||||
};
|
||||
|
||||
enum AudioSystemFile {
|
||||
DefaultSound,
|
||||
IM,
|
||||
Mail,
|
||||
Reminder,
|
||||
SMS,
|
||||
Alarm,
|
||||
Alarm2,
|
||||
Alarm3,
|
||||
Alarm4,
|
||||
Alarm5,
|
||||
Alarm6,
|
||||
Alarm7,
|
||||
Alarm8,
|
||||
Alarm9,
|
||||
Alarm10,
|
||||
Call,
|
||||
Call1,
|
||||
Call2,
|
||||
Call3,
|
||||
Call4,
|
||||
Call5,
|
||||
Call6,
|
||||
Call7,
|
||||
Call8,
|
||||
Call9,
|
||||
Call10,
|
||||
};
|
||||
|
||||
|
||||
WinToastTemplate(_In_ WinToastTemplateType type = WinToastTemplateType::ImageAndText02);
|
||||
~WinToastTemplate();
|
||||
|
||||
void setFirstLine(_In_ const std::wstring& text);
|
||||
void setSecondLine(_In_ const std::wstring& text);
|
||||
void setThirdLine(_In_ const std::wstring& text);
|
||||
void setTextField(_In_ const std::wstring& txt, _In_ TextField pos);
|
||||
void setAttributionText(_In_ const std::wstring& attributionText);
|
||||
void setImagePath(_In_ const std::wstring& imgPath);
|
||||
void setAudioPath(_In_ WinToastTemplate::AudioSystemFile audio);
|
||||
void setAudioPath(_In_ const std::wstring& audioPath);
|
||||
void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption);
|
||||
void setDuration(_In_ Duration duration);
|
||||
void setExpiration(_In_ INT64 millisecondsFromNow);
|
||||
void setScenario(_In_ Scenario scenario);
|
||||
void addAction(_In_ const std::wstring& label);
|
||||
|
||||
std::size_t textFieldsCount() const;
|
||||
std::size_t actionsCount() const;
|
||||
bool hasImage() const;
|
||||
const std::vector<std::wstring>& textFields() const;
|
||||
const std::wstring& textField(_In_ TextField pos) const;
|
||||
const std::wstring& actionLabel(_In_ std::size_t pos) const;
|
||||
const std::wstring& imagePath() const;
|
||||
const std::wstring& audioPath() const;
|
||||
const std::wstring& attributionText() const;
|
||||
const std::wstring& scenario() const;
|
||||
INT64 expiration() const;
|
||||
WinToastTemplateType type() const;
|
||||
WinToastTemplate::AudioOption audioOption() const;
|
||||
Duration duration() const;
|
||||
private:
|
||||
std::vector<std::wstring> _textFields{};
|
||||
std::vector<std::wstring> _actions{};
|
||||
std::wstring _imagePath{};
|
||||
std::wstring _audioPath{};
|
||||
std::wstring _attributionText{};
|
||||
std::wstring _scenario{L"Default"};
|
||||
INT64 _expiration{0};
|
||||
AudioOption _audioOption{WinToastTemplate::AudioOption::Default};
|
||||
WinToastTemplateType _type{WinToastTemplateType::Text01};
|
||||
Duration _duration{Duration::System};
|
||||
};
|
||||
|
||||
class WinToast {
|
||||
public:
|
||||
enum WinToastError {
|
||||
NoError = 0,
|
||||
NotInitialized,
|
||||
SystemNotSupported,
|
||||
ShellLinkNotCreated,
|
||||
InvalidAppUserModelID,
|
||||
InvalidParameters,
|
||||
InvalidHandler,
|
||||
NotDisplayed,
|
||||
UnknownError
|
||||
};
|
||||
|
||||
enum ShortcutResult {
|
||||
SHORTCUT_UNCHANGED = 0,
|
||||
SHORTCUT_WAS_CHANGED = 1,
|
||||
SHORTCUT_WAS_CREATED = 2,
|
||||
|
||||
SHORTCUT_MISSING_PARAMETERS = -1,
|
||||
SHORTCUT_INCOMPATIBLE_OS = -2,
|
||||
SHORTCUT_COM_INIT_FAILURE = -3,
|
||||
SHORTCUT_CREATE_FAILED = -4
|
||||
};
|
||||
|
||||
enum ShortcutPolicy {
|
||||
/* Don't check, create, or modify a shortcut. */
|
||||
SHORTCUT_POLICY_IGNORE = 0,
|
||||
/* Require a shortcut with matching AUMI, don't create or modify an existing one. */
|
||||
SHORTCUT_POLICY_REQUIRE_NO_CREATE = 1,
|
||||
/* Require a shortcut with matching AUMI, create if missing, modify if not matching.
|
||||
* This is the default. */
|
||||
SHORTCUT_POLICY_REQUIRE_CREATE = 2,
|
||||
};
|
||||
|
||||
WinToast(void);
|
||||
virtual ~WinToast();
|
||||
static WinToast* instance();
|
||||
static bool isCompatible();
|
||||
static bool isSupportingModernFeatures();
|
||||
static std::wstring configureAUMI(_In_ const std::wstring& companyName,
|
||||
_In_ const std::wstring& productName,
|
||||
_In_ const std::wstring& subProduct = std::wstring(),
|
||||
_In_ const std::wstring& versionInformation = std::wstring());
|
||||
static const std::wstring& strerror(_In_ WinToastError error);
|
||||
virtual bool initialize(_Out_ WinToastError* error = nullptr);
|
||||
virtual bool isInitialized() const;
|
||||
virtual bool hideToast(_In_ INT64 id);
|
||||
virtual INT64 showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_ WinToastError* error = nullptr);
|
||||
virtual void clear();
|
||||
virtual enum ShortcutResult createShortcut();
|
||||
|
||||
const std::wstring& appName() const;
|
||||
const std::wstring& appUserModelId() const;
|
||||
void setAppUserModelId(_In_ const std::wstring& aumi);
|
||||
void setAppName(_In_ const std::wstring& appName);
|
||||
void setShortcutPolicy(_In_ ShortcutPolicy policy);
|
||||
|
||||
protected:
|
||||
bool _isInitialized{false};
|
||||
bool _hasCoInitialized{false};
|
||||
ShortcutPolicy _shortcutPolicy{SHORTCUT_POLICY_REQUIRE_CREATE};
|
||||
std::wstring _appName{};
|
||||
std::wstring _aumi{};
|
||||
std::map<INT64, ComPtr<IToastNotification>> _buffer{};
|
||||
|
||||
HRESULT validateShellLinkHelper(_Out_ bool& wasChanged);
|
||||
HRESULT createShellLinkHelper();
|
||||
HRESULT setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path);
|
||||
HRESULT setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default);
|
||||
HRESULT setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos);
|
||||
HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text);
|
||||
HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& action, _In_ const std::wstring& arguments);
|
||||
HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration);
|
||||
HRESULT addScenarioHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& scenario);
|
||||
ComPtr<IToastNotifier> notifier(_In_ bool* succeded) const;
|
||||
void setError(_Out_ WinToastError* error, _In_ WinToastError value);
|
||||
};
|
||||
}
|
||||
#endif // WINTOASTLIB_H
|
|
@ -88,6 +88,9 @@ with Files('rlbox_wasm2c_sandbox/**'):
|
|||
with Files('wasm2c/**'):
|
||||
BUG_COMPONENT = ('Core', 'Security: RLBox')
|
||||
|
||||
with Files('WinToast/**'):
|
||||
BUG_COMPONENT = ('Toolkit', 'General')
|
||||
|
||||
with Files('libsrtp/**'):
|
||||
BUG_COMPONENT = ('Core', 'WebRTC: Networking')
|
||||
|
||||
|
|
|
@ -153,6 +153,9 @@
|
|||
<li><a href="about:license#validator">Validator License</a></li>
|
||||
<li><a href="about:license#vtune">VTune License</a></li>
|
||||
<li><a href="about:license#webrtc">WebRTC License</a></li>
|
||||
#ifdef MOZ_DEFAULT_BROWSER_AGENT
|
||||
<li><a href="about:license#wintoast">WinToast License</a></li>
|
||||
#endif
|
||||
<li><a href="about:license#x264">x264 License</a></li>
|
||||
<li><a href="about:license#xiph">Xiph.org Foundation License</a></li>
|
||||
</ul>
|
||||
|
@ -3616,6 +3619,9 @@ SOFTWARE.
|
|||
<li><code>third_party/rust/synstructure</code></li>
|
||||
<li><code>third_party/rust/void</code></li>
|
||||
<li><code>js/src/zydis</code> (unless otherwise specified)</li>
|
||||
#ifdef MOZ_DEFAULT_BROWSER_AGENT
|
||||
<li><code>third_party/WinToast</code> unless otherwise specified</li>
|
||||
#endif
|
||||
</ul>
|
||||
See the individual LICENSE files or headers for copyright owners.</p>
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ const EXIT_CODE = {
|
|||
const lazy = {};
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
setTimeout: "resource://gre/modules/Timer.sys.mjs",
|
||||
BackgroundTasksUtils: "resource://gre/modules/BackgroundTasksUtils.sys.mjs",
|
||||
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
// eslint-disable-next-line mozilla/no-browser-refs-in-toolkit
|
||||
ShellService: "resource:///modules/ShellService.sys.mjs",
|
||||
});
|
||||
|
@ -155,11 +157,25 @@ export async function runBackgroundTask(commandLine) {
|
|||
|
||||
lazy.log.info(`Running do-task with AUMID "${aumid}"`);
|
||||
|
||||
let cppFallback = false;
|
||||
try {
|
||||
lazy.log.info("Running JS do-task.");
|
||||
await runWithRegistryLocked(async () => {
|
||||
await doTask(defaultAgent, force);
|
||||
});
|
||||
await lazy.BackgroundTasksUtils.enableNimbus(commandLine);
|
||||
cppFallback =
|
||||
lazy.NimbusFeatures.defaultAgent.getVariable("cppFallback");
|
||||
} catch (e) {
|
||||
lazy.log.error(`Error enabling nimbus: ${e}`);
|
||||
}
|
||||
|
||||
try {
|
||||
if (!cppFallback) {
|
||||
lazy.log.info("Running JS do-task.");
|
||||
await runWithRegistryLocked(async () => {
|
||||
await doTask(defaultAgent, force);
|
||||
});
|
||||
} else {
|
||||
lazy.log.info("Running C++ do-task.");
|
||||
defaultAgent.doTask(aumid, force);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.message) {
|
||||
lazy.log.error(e.message);
|
||||
|
|
|
@ -7,8 +7,11 @@
|
|||
#include <windows.h>
|
||||
#include <shlwapi.h>
|
||||
#include <objbase.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
#include "nsAutoRef.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
#include "nsString.h"
|
||||
|
@ -300,6 +303,62 @@ DefaultAgent::Uninstall(const nsAString& aUniqueToken) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DefaultAgent::DoTask(const nsAString& aUniqueToken, const bool aForce) {
|
||||
// Acquire() has a short timeout. Since this runs in the background, we
|
||||
// could use a longer timeout in this situation. However, if another
|
||||
// installation's agent is already running, it will update CurrentDefault,
|
||||
// possibly send a ping, and possibly show a notification.
|
||||
// Once all that has happened, there is no real reason to do it again. We
|
||||
// only send one ping per day, so we aren't going to do that again. And
|
||||
// the only time we ever show a second notification is 7 days after the
|
||||
// first one, so we aren't going to do that again either.
|
||||
// If the other process didn't take those actions, there is no reason that
|
||||
// this process would take them.
|
||||
// If the other process fails, this one will most likely fail for the same
|
||||
// reason.
|
||||
// So we'll just bail if we can't get the mutex quickly.
|
||||
RegistryMutex regMutex;
|
||||
if (!regMutex.Acquire()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// Check that Firefox ran recently, if not then stop here.
|
||||
// Also stop if no timestamp was found, which most likely indicates
|
||||
// that Firefox was not yet run.
|
||||
bool ranRecently = false;
|
||||
if (!aForce && (!CheckIfAppRanRecently(&ranRecently) || !ranRecently)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
DefaultBrowserResult defaultBrowserResult = GetDefaultBrowserInfo();
|
||||
DefaultBrowserInfo browserInfo{};
|
||||
if (defaultBrowserResult.isOk()) {
|
||||
browserInfo = defaultBrowserResult.unwrap();
|
||||
} else {
|
||||
browserInfo.currentDefaultBrowser = Browser::Error;
|
||||
browserInfo.previousDefaultBrowser = Browser::Error;
|
||||
}
|
||||
|
||||
DefaultPdfResult defaultPdfResult = GetDefaultPdfInfo();
|
||||
DefaultPdfInfo pdfInfo{};
|
||||
if (defaultPdfResult.isOk()) {
|
||||
pdfInfo = defaultPdfResult.unwrap();
|
||||
} else {
|
||||
pdfInfo.currentDefaultPdf = PDFHandler::Error;
|
||||
}
|
||||
|
||||
NotificationActivities activitiesPerformed;
|
||||
// We block while waiting for the notification which prevents STA thread
|
||||
// callbacks from running as the event loop won't run. Moving notification
|
||||
// handling to an MTA thread prevents this conflict.
|
||||
activitiesPerformed = MaybeShowNotification(
|
||||
browserInfo, PromiseFlatString(aUniqueToken).get(), aForce);
|
||||
|
||||
HRESULT hr = SendDefaultAgentPing(browserInfo, pdfInfo, activitiesPerformed);
|
||||
return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DefaultAgent::AppRanRecently(bool* aRanRecently) {
|
||||
bool ranRecently = false;
|
||||
|
|
|
@ -184,6 +184,17 @@ BrowserResult TryGetReplacePreviousDefaultBrowser(Browser currentDefault) {
|
|||
return GetBrowserFromString(previousDefault);
|
||||
}
|
||||
|
||||
DefaultBrowserResult GetDefaultBrowserInfo() {
|
||||
DefaultBrowserInfo browserInfo;
|
||||
|
||||
MOZ_TRY_VAR(browserInfo.currentDefaultBrowser, TryGetDefaultBrowser());
|
||||
MOZ_TRY_VAR(
|
||||
browserInfo.previousDefaultBrowser,
|
||||
TryGetReplacePreviousDefaultBrowser(browserInfo.currentDefaultBrowser));
|
||||
|
||||
return browserInfo;
|
||||
}
|
||||
|
||||
// We used to prefix this key with the installation directory, but that causes
|
||||
// problems with our new "only one ping per day across installs" restriction.
|
||||
// To make sure all installations use consistent data, the value's name is
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <string>
|
||||
|
||||
#include "mozilla/DefineEnum.h"
|
||||
#include "mozilla/WinHeaderOnlyUtils.h"
|
||||
|
||||
namespace mozilla::default_agent {
|
||||
|
||||
|
@ -23,6 +24,9 @@ struct DefaultBrowserInfo {
|
|||
Browser previousDefaultBrowser;
|
||||
};
|
||||
|
||||
using DefaultBrowserResult = mozilla::WindowsErrorResult<DefaultBrowserInfo>;
|
||||
|
||||
DefaultBrowserResult GetDefaultBrowserInfo();
|
||||
Browser GetDefaultBrowser();
|
||||
Browser GetReplacePreviousDefaultBrowser(Browser currentBrowser);
|
||||
|
||||
|
|
|
@ -7,10 +7,36 @@
|
|||
#include "Notification.h"
|
||||
|
||||
#include <shlwapi.h>
|
||||
#include <wchar.h>
|
||||
#include <windows.h>
|
||||
#include <winnt.h>
|
||||
|
||||
#include "nsLiteralString.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/CmdLineAndEnvUtils.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/mscom/EnsureMTA.h"
|
||||
#include "mozilla/intl/FileSource.h"
|
||||
#include "mozilla/intl/Localization.h"
|
||||
#include "mozilla/ShellHeaderOnlyUtils.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/WinHeaderOnlyUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
#include "readstrings.h"
|
||||
#include "updatererrors.h"
|
||||
#include "WindowsDefaultBrowser.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "DefaultBrowser.h"
|
||||
#include "EventLog.h"
|
||||
#include "Registry.h"
|
||||
#include "SetDefaultBrowser.h"
|
||||
|
||||
#include "wintoastlib.h"
|
||||
|
||||
using mozilla::intl::Localization;
|
||||
|
||||
#define SEVEN_DAYS_IN_SECONDS (7 * 24 * 60 * 60)
|
||||
|
||||
|
@ -23,6 +49,574 @@
|
|||
|
||||
namespace mozilla::default_agent {
|
||||
|
||||
bool FirefoxInstallIsEnglish();
|
||||
|
||||
static bool SetInitialNotificationShown(bool wasShown) {
|
||||
return !RegistrySetValueBool(IsPrefixed::Unprefixed,
|
||||
L"InitialNotificationShown", wasShown)
|
||||
.isErr();
|
||||
}
|
||||
|
||||
static bool GetInitialNotificationShown() {
|
||||
return RegistryGetValueBool(IsPrefixed::Unprefixed,
|
||||
L"InitialNotificationShown")
|
||||
.unwrapOr(mozilla::Some(false))
|
||||
.valueOr(false);
|
||||
}
|
||||
|
||||
static bool ResetInitialNotificationShown() {
|
||||
return RegistryDeleteValue(IsPrefixed::Unprefixed,
|
||||
L"InitialNotificationShown")
|
||||
.isOk();
|
||||
}
|
||||
|
||||
static bool SetFollowupNotificationShown(bool wasShown) {
|
||||
return !RegistrySetValueBool(IsPrefixed::Unprefixed,
|
||||
L"FollowupNotificationShown", wasShown)
|
||||
.isErr();
|
||||
}
|
||||
|
||||
static bool GetFollowupNotificationShown() {
|
||||
return RegistryGetValueBool(IsPrefixed::Unprefixed,
|
||||
L"FollowupNotificationShown")
|
||||
.unwrapOr(mozilla::Some(false))
|
||||
.valueOr(false);
|
||||
}
|
||||
|
||||
static bool SetFollowupNotificationSuppressed(bool value) {
|
||||
return !RegistrySetValueBool(IsPrefixed::Unprefixed,
|
||||
L"FollowupNotificationSuppressed", value)
|
||||
.isErr();
|
||||
}
|
||||
|
||||
static bool GetFollowupNotificationSuppressed() {
|
||||
return RegistryGetValueBool(IsPrefixed::Unprefixed,
|
||||
L"FollowupNotificationSuppressed")
|
||||
.unwrapOr(mozilla::Some(false))
|
||||
.valueOr(false);
|
||||
}
|
||||
|
||||
// Returns 0 if no value is set.
|
||||
static ULONGLONG GetFollowupNotificationRequestTime() {
|
||||
return RegistryGetValueQword(IsPrefixed::Unprefixed, L"FollowupRequestTime")
|
||||
.unwrapOr(mozilla::Some(0))
|
||||
.valueOr(0);
|
||||
}
|
||||
|
||||
// Returns false if no value is set.
|
||||
static bool GetPrefSetDefaultBrowserUserChoice() {
|
||||
return RegistryGetValueBool(IsPrefixed::Prefixed,
|
||||
L"SetDefaultBrowserUserChoice")
|
||||
.unwrapOr(mozilla::Some(false))
|
||||
.valueOr(false);
|
||||
}
|
||||
|
||||
struct ToastStrings {
|
||||
mozilla::UniquePtr<wchar_t[]> text1;
|
||||
mozilla::UniquePtr<wchar_t[]> text2;
|
||||
mozilla::UniquePtr<wchar_t[]> action1;
|
||||
mozilla::UniquePtr<wchar_t[]> action2;
|
||||
mozilla::UniquePtr<wchar_t[]> relImagePath;
|
||||
};
|
||||
|
||||
struct Strings {
|
||||
// Toast notification button text is hard to localize because it tends to
|
||||
// overflow. Thus, we have 3 different toast notifications:
|
||||
// - The initial notification, which includes a button with text like
|
||||
// "Ask me later". Since we cannot easily localize this, we will display
|
||||
// it only in English.
|
||||
// - The followup notification, to be shown if the user clicked "Ask me
|
||||
// later". Since we only have that button in English, we only need this
|
||||
// notification in English.
|
||||
// - The localized notification, which has much shorter button text to
|
||||
// (hopefully) prevent overflow: just "Yes" and "No". Since we no longer
|
||||
// have an "Ask me later" button, a followup localized notification is not
|
||||
// needed.
|
||||
ToastStrings initialToast;
|
||||
ToastStrings followupToast;
|
||||
ToastStrings localizedToast;
|
||||
|
||||
// Returned pointer points within this struct and should not be freed.
|
||||
const ToastStrings* GetToastStrings(NotificationType whichToast,
|
||||
bool englishStrings) const {
|
||||
if (!englishStrings) {
|
||||
return &localizedToast;
|
||||
}
|
||||
if (whichToast == NotificationType::Initial) {
|
||||
return &initialToast;
|
||||
}
|
||||
return &followupToast;
|
||||
}
|
||||
};
|
||||
|
||||
// Gets all strings out of the relevant INI files.
|
||||
// Returns true on success, false on failure
|
||||
static bool GetStrings(Strings& strings) {
|
||||
mozilla::UniquePtr<wchar_t[]> installPath;
|
||||
bool success = GetInstallDirectory(installPath);
|
||||
if (!success) {
|
||||
LOG_ERROR_MESSAGE(L"Failed to get install directory when getting strings");
|
||||
return false;
|
||||
}
|
||||
nsTArray<nsCString> resIds = {"branding/brand.ftl"_ns,
|
||||
"browser/backgroundtasks/defaultagent.ftl"_ns};
|
||||
RefPtr<Localization> l10n = Localization::Create(resIds, true);
|
||||
nsAutoCString daHeaderText, daBodyText, daYesButton, daNoButton;
|
||||
mozilla::ErrorResult daRv;
|
||||
l10n->FormatValueSync("default-browser-notification-header-text"_ns, {},
|
||||
daHeaderText, daRv);
|
||||
ENSURE_SUCCESS(daRv, false);
|
||||
l10n->FormatValueSync("default-browser-notification-body-text"_ns, {},
|
||||
daBodyText, daRv);
|
||||
ENSURE_SUCCESS(daRv, false);
|
||||
l10n->FormatValueSync("default-browser-notification-yes-button-text"_ns, {},
|
||||
daYesButton, daRv);
|
||||
ENSURE_SUCCESS(daRv, false);
|
||||
l10n->FormatValueSync("default-browser-notification-no-button-text"_ns, {},
|
||||
daNoButton, daRv);
|
||||
ENSURE_SUCCESS(daRv, false);
|
||||
|
||||
NS_ConvertUTF8toUTF16 daHeaderTextW(daHeaderText), daBodyTextW(daBodyText),
|
||||
daYesButtonW(daYesButton), daNoButtonW(daNoButton);
|
||||
strings.localizedToast.text1 =
|
||||
mozilla::MakeUnique<wchar_t[]>(daHeaderTextW.Length() + 1);
|
||||
wcsncpy(strings.localizedToast.text1.get(), daHeaderTextW.get(),
|
||||
daHeaderTextW.Length() + 1);
|
||||
strings.localizedToast.text2 =
|
||||
mozilla::MakeUnique<wchar_t[]>(daBodyTextW.Length() + 1);
|
||||
wcsncpy(strings.localizedToast.text2.get(), daBodyTextW.get(),
|
||||
daBodyTextW.Length() + 1);
|
||||
strings.localizedToast.action1 =
|
||||
mozilla::MakeUnique<wchar_t[]>(daYesButtonW.Length() + 1);
|
||||
wcsncpy(strings.localizedToast.action1.get(), daYesButtonW.get(),
|
||||
daYesButtonW.Length() + 1);
|
||||
strings.localizedToast.action2 =
|
||||
mozilla::MakeUnique<wchar_t[]>(daNoButtonW.Length() + 1);
|
||||
wcsncpy(strings.localizedToast.action2.get(), daNoButtonW.get(),
|
||||
daNoButtonW.Length() + 1);
|
||||
const wchar_t* iniFormat = L"%s\\defaultagent.ini";
|
||||
int bufferSize = _scwprintf(iniFormat, installPath.get());
|
||||
++bufferSize; // Extra character for terminating null
|
||||
mozilla::UniquePtr<wchar_t[]> iniPath =
|
||||
mozilla::MakeUnique<wchar_t[]>(bufferSize);
|
||||
_snwprintf_s(iniPath.get(), bufferSize, _TRUNCATE, iniFormat,
|
||||
installPath.get());
|
||||
|
||||
IniReader nonlocalizedReader(iniPath.get(), "Nonlocalized");
|
||||
nonlocalizedReader.AddKey("InitialToastRelativeImagePath",
|
||||
&strings.initialToast.relImagePath);
|
||||
nonlocalizedReader.AddKey("FollowupToastRelativeImagePath",
|
||||
&strings.followupToast.relImagePath);
|
||||
nonlocalizedReader.AddKey("LocalizedToastRelativeImagePath",
|
||||
&strings.localizedToast.relImagePath);
|
||||
int result = nonlocalizedReader.Read();
|
||||
if (result != OK) {
|
||||
LOG_ERROR_MESSAGE(L"Unable to read non-localized strings: %d", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static mozilla::WindowsError LaunchFirefoxToHandleDefaultBrowserAgent() {
|
||||
// Could also be `MOZ_APP_NAME.exe`, but there's no generality to be gained:
|
||||
// the WDBA is Firefox-only.
|
||||
FilePathResult firefoxPathResult = GetRelativeBinaryPath(L"firefox.exe");
|
||||
if (firefoxPathResult.isErr()) {
|
||||
return firefoxPathResult.unwrapErr();
|
||||
}
|
||||
std::wstring firefoxPath = firefoxPathResult.unwrap();
|
||||
|
||||
_bstr_t cmd = firefoxPath.c_str();
|
||||
// Omit argv[0] because ShellExecute doesn't need it.
|
||||
_variant_t args(L"-to-handle-default-browser-agent");
|
||||
_variant_t operation(L"open");
|
||||
_variant_t directory;
|
||||
_variant_t showCmd(SW_SHOWNORMAL);
|
||||
|
||||
// To prevent inheriting environment variables from the background task, we
|
||||
// run Firefox via Explorer instead of our own process. This mimics the
|
||||
// implementation of the Windows Launcher Process.
|
||||
auto result =
|
||||
ShellExecuteByExplorer(cmd, args, operation, directory, showCmd);
|
||||
NS_ENSURE_TRUE(result.isOk(), result.unwrapErr());
|
||||
|
||||
return mozilla::WindowsError::CreateSuccess();
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the default browser.
|
||||
*
|
||||
* First check if we can directly write UserChoice, if so attempt that.
|
||||
* If we can't write UserChoice, or if the attempt fails, fall back to
|
||||
* showing the Default Apps page of Settings.
|
||||
*
|
||||
* @param aAumi The AUMI of the installation to set as default.
|
||||
*/
|
||||
static void SetDefaultBrowserFromNotification(const wchar_t* aumi) {
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
if (GetPrefSetDefaultBrowserUserChoice()) {
|
||||
rv = SetDefaultBrowserUserChoice(aumi);
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mozilla::Unused << LaunchFirefoxToHandleDefaultBrowserAgent();
|
||||
} else {
|
||||
LOG_ERROR_MESSAGE(L"Failed to SetDefaultBrowserUserChoice: %#X",
|
||||
GetLastError());
|
||||
LaunchModernSettingsDialogDefaultApps();
|
||||
}
|
||||
}
|
||||
|
||||
// This encapsulates the data that needs to be protected by a mutex because it
|
||||
// will be shared by the main thread and the handler thread.
|
||||
// To ensure the data is only written once, handlerDataHasBeenSet should be
|
||||
// initialized to false, then set to true when the handler writes data into the
|
||||
// structure.
|
||||
struct HandlerData {
|
||||
NotificationActivities activitiesPerformed;
|
||||
bool handlerDataHasBeenSet;
|
||||
};
|
||||
|
||||
// The value that ToastHandler writes into should be a global. We can't control
|
||||
// when ToastHandler is called, and if this value isn't a global, ToastHandler
|
||||
// may be called and attempt to access this after it has been deconstructed.
|
||||
// Since this value is accessed by the handler thread and the main thread, it
|
||||
// is protected by a mutex (gHandlerMutex).
|
||||
// Since ShowNotification deconstructs the mutex, it might seem like once
|
||||
// ShowNotification exits, we can just rely on the inability to wait on an
|
||||
// invalid mutex to protect the deconstructed data, but it's possible that
|
||||
// we could deconstruct the mutex while the handler is holding it and is
|
||||
// already accessing the protected data.
|
||||
static HandlerData gHandlerReturnData;
|
||||
static HANDLE gHandlerMutex = INVALID_HANDLE_VALUE;
|
||||
|
||||
class ToastHandler : public WinToastLib::IWinToastHandler {
|
||||
private:
|
||||
NotificationType mWhichNotification;
|
||||
HANDLE mEvent;
|
||||
const std::wstring mAumiStr;
|
||||
|
||||
public:
|
||||
ToastHandler(NotificationType whichNotification, HANDLE event,
|
||||
const wchar_t* aumi)
|
||||
: mWhichNotification(whichNotification), mEvent(event), mAumiStr(aumi) {}
|
||||
|
||||
void FinishHandler(NotificationActivities& returnData) const {
|
||||
SetReturnData(returnData);
|
||||
|
||||
BOOL success = SetEvent(mEvent);
|
||||
if (!success) {
|
||||
LOG_ERROR_MESSAGE(L"Event could not be set: %#X", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
void SetReturnData(NotificationActivities& toSet) const {
|
||||
DWORD result = WaitForSingleObject(gHandlerMutex, MUTEX_TIMEOUT_MS);
|
||||
if (result == WAIT_TIMEOUT) {
|
||||
LOG_ERROR_MESSAGE(L"Unable to obtain mutex ownership");
|
||||
return;
|
||||
} else if (result == WAIT_FAILED) {
|
||||
LOG_ERROR_MESSAGE(L"Failed to wait on mutex: %#X", GetLastError());
|
||||
return;
|
||||
} else if (result == WAIT_ABANDONED) {
|
||||
LOG_ERROR_MESSAGE(L"Found abandoned mutex");
|
||||
ReleaseMutex(gHandlerMutex);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only set this data once
|
||||
if (!gHandlerReturnData.handlerDataHasBeenSet) {
|
||||
gHandlerReturnData.activitiesPerformed = toSet;
|
||||
gHandlerReturnData.handlerDataHasBeenSet = true;
|
||||
}
|
||||
|
||||
BOOL success = ReleaseMutex(gHandlerMutex);
|
||||
if (!success) {
|
||||
LOG_ERROR_MESSAGE(L"Unable to release mutex ownership: %#X",
|
||||
GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
void toastActivated() const override {
|
||||
NotificationActivities activitiesPerformed;
|
||||
activitiesPerformed.type = mWhichNotification;
|
||||
activitiesPerformed.shown = NotificationShown::Shown;
|
||||
activitiesPerformed.action = NotificationAction::ToastClicked;
|
||||
|
||||
// Notification strings are written to indicate the default browser is
|
||||
// restored to Firefox when the notification body is clicked to prevent
|
||||
// ambiguity when buttons aren't pressed.
|
||||
SetDefaultBrowserFromNotification(mAumiStr.c_str());
|
||||
|
||||
FinishHandler(activitiesPerformed);
|
||||
}
|
||||
|
||||
void toastActivated(int actionIndex) const override {
|
||||
NotificationActivities activitiesPerformed;
|
||||
activitiesPerformed.type = mWhichNotification;
|
||||
activitiesPerformed.shown = NotificationShown::Shown;
|
||||
// Override this below
|
||||
activitiesPerformed.action = NotificationAction::NoAction;
|
||||
|
||||
if (actionIndex == 0) {
|
||||
// "Make Firefox the default" button, on both the initial and followup
|
||||
// notifications. "Yes" button on the localized notification.
|
||||
activitiesPerformed.action = NotificationAction::MakeFirefoxDefaultButton;
|
||||
|
||||
SetDefaultBrowserFromNotification(mAumiStr.c_str());
|
||||
} else if (actionIndex == 1) {
|
||||
// Do nothing. As long as we don't call
|
||||
// SetFollowupNotificationRequestTime, there will be no followup
|
||||
// notification.
|
||||
activitiesPerformed.action = NotificationAction::DismissedByButton;
|
||||
}
|
||||
|
||||
FinishHandler(activitiesPerformed);
|
||||
}
|
||||
|
||||
void toastDismissed(WinToastDismissalReason state) const override {
|
||||
NotificationActivities activitiesPerformed;
|
||||
activitiesPerformed.type = mWhichNotification;
|
||||
activitiesPerformed.shown = NotificationShown::Shown;
|
||||
// Override this below
|
||||
activitiesPerformed.action = NotificationAction::NoAction;
|
||||
|
||||
if (state == WinToastDismissalReason::TimedOut) {
|
||||
activitiesPerformed.action = NotificationAction::DismissedByTimeout;
|
||||
} else if (state == WinToastDismissalReason::ApplicationHidden) {
|
||||
activitiesPerformed.action =
|
||||
NotificationAction::DismissedByApplicationHidden;
|
||||
} else if (state == WinToastDismissalReason::UserCanceled) {
|
||||
activitiesPerformed.action = NotificationAction::DismissedToActionCenter;
|
||||
}
|
||||
|
||||
FinishHandler(activitiesPerformed);
|
||||
}
|
||||
|
||||
void toastFailed() const override {
|
||||
NotificationActivities activitiesPerformed;
|
||||
activitiesPerformed.type = mWhichNotification;
|
||||
activitiesPerformed.shown = NotificationShown::Error;
|
||||
activitiesPerformed.action = NotificationAction::NoAction;
|
||||
|
||||
LOG_ERROR_MESSAGE(L"Toast notification failed to display");
|
||||
FinishHandler(activitiesPerformed);
|
||||
}
|
||||
};
|
||||
|
||||
// This function blocks until the shown notification is activated or dismissed.
|
||||
static NotificationActivities ShowNotification(
|
||||
NotificationType whichNotification, const wchar_t* aumi) {
|
||||
// Initially set the value that will be returned to error. If the notification
|
||||
// is shown successfully, we'll update it.
|
||||
NotificationActivities activitiesPerformed = {whichNotification,
|
||||
NotificationShown::Error,
|
||||
NotificationAction::NoAction};
|
||||
|
||||
bool isEnglishInstall = FirefoxInstallIsEnglish();
|
||||
|
||||
Strings strings;
|
||||
if (!GetStrings(strings)) {
|
||||
return activitiesPerformed;
|
||||
}
|
||||
const ToastStrings* toastStrings =
|
||||
strings.GetToastStrings(whichNotification, isEnglishInstall);
|
||||
|
||||
mozilla::mscom::EnsureMTA([&] {
|
||||
using namespace WinToastLib;
|
||||
|
||||
if (!WinToast::isCompatible()) {
|
||||
LOG_ERROR_MESSAGE(L"System is not compatible with WinToast");
|
||||
return;
|
||||
}
|
||||
WinToast::instance()->setAppName(L"" MOZ_APP_DISPLAYNAME);
|
||||
std::wstring aumiStr = aumi;
|
||||
WinToast::instance()->setAppUserModelId(aumiStr);
|
||||
WinToast::instance()->setShortcutPolicy(
|
||||
WinToastLib::WinToast::SHORTCUT_POLICY_REQUIRE_NO_CREATE);
|
||||
WinToast::WinToastError error;
|
||||
if (!WinToast::instance()->initialize(&error)) {
|
||||
LOG_ERROR_MESSAGE(WinToast::strerror(error).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// This event object will let the handler notify us when it has handled the
|
||||
// notification.
|
||||
nsAutoHandle event(CreateEventW(nullptr, TRUE, FALSE, nullptr));
|
||||
if (event.get() == nullptr) {
|
||||
LOG_ERROR_MESSAGE(L"Unable to create event object: %#X", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (whichNotification == NotificationType::Initial) {
|
||||
success = SetInitialNotificationShown(true);
|
||||
} else {
|
||||
success = SetFollowupNotificationShown(true);
|
||||
}
|
||||
if (!success) {
|
||||
// Return early in this case to prevent the notification from being shown
|
||||
// on every run.
|
||||
LOG_ERROR_MESSAGE(L"Unable to set notification as displayed");
|
||||
return;
|
||||
}
|
||||
|
||||
// We need the absolute image path, not the relative path.
|
||||
mozilla::UniquePtr<wchar_t[]> installPath;
|
||||
success = GetInstallDirectory(installPath);
|
||||
if (!success) {
|
||||
LOG_ERROR_MESSAGE(L"Failed to get install directory for the image path");
|
||||
return;
|
||||
}
|
||||
const wchar_t* absPathFormat = L"%s\\%s";
|
||||
int bufferSize = _scwprintf(absPathFormat, installPath.get(),
|
||||
toastStrings->relImagePath.get());
|
||||
++bufferSize; // Extra character for terminating null
|
||||
mozilla::UniquePtr<wchar_t[]> absImagePath =
|
||||
mozilla::MakeUnique<wchar_t[]>(bufferSize);
|
||||
_snwprintf_s(absImagePath.get(), bufferSize, _TRUNCATE, absPathFormat,
|
||||
installPath.get(), toastStrings->relImagePath.get());
|
||||
|
||||
// This is used to protect gHandlerReturnData.
|
||||
gHandlerMutex = CreateMutexW(nullptr, TRUE, nullptr);
|
||||
if (gHandlerMutex == nullptr) {
|
||||
LOG_ERROR_MESSAGE(L"Unable to create mutex: %#X", GetLastError());
|
||||
return;
|
||||
}
|
||||
// Automatically close this mutex when this function exits.
|
||||
nsAutoHandle autoMutex(gHandlerMutex);
|
||||
// No need to initialize gHandlerReturnData.activitiesPerformed, since it
|
||||
// will be set by the handler. But we do need to initialize
|
||||
// gHandlerReturnData.handlerDataHasBeenSet so the handler knows that no
|
||||
// data has been set yet.
|
||||
gHandlerReturnData.handlerDataHasBeenSet = false;
|
||||
success = ReleaseMutex(gHandlerMutex);
|
||||
if (!success) {
|
||||
LOG_ERROR_MESSAGE(L"Unable to release mutex ownership: %#X",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
// Finally ready to assemble the notification and dispatch it.
|
||||
WinToastTemplate toastTemplate =
|
||||
WinToastTemplate(WinToastTemplate::ImageAndText02);
|
||||
toastTemplate.setTextField(toastStrings->text1.get(),
|
||||
WinToastTemplate::FirstLine);
|
||||
toastTemplate.setTextField(toastStrings->text2.get(),
|
||||
WinToastTemplate::SecondLine);
|
||||
toastTemplate.addAction(toastStrings->action1.get());
|
||||
toastTemplate.addAction(toastStrings->action2.get());
|
||||
toastTemplate.setImagePath(absImagePath.get());
|
||||
toastTemplate.setScenario(WinToastTemplate::Scenario::Reminder);
|
||||
ToastHandler* handler =
|
||||
new ToastHandler(whichNotification, event.get(), aumi);
|
||||
INT64 id = WinToast::instance()->showToast(toastTemplate, handler, &error);
|
||||
if (id < 0) {
|
||||
LOG_ERROR_MESSAGE(WinToast::strerror(error).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD result =
|
||||
WaitForSingleObject(event.get(), NOTIFICATION_WAIT_TIMEOUT_MS);
|
||||
// Don't return after these errors. Attempt to hide the notification.
|
||||
if (result == WAIT_FAILED) {
|
||||
LOG_ERROR_MESSAGE(L"Unable to wait on event object: %#X", GetLastError());
|
||||
} else if (result == WAIT_TIMEOUT) {
|
||||
LOG_ERROR_MESSAGE(L"Timed out waiting for event object");
|
||||
} else {
|
||||
result = WaitForSingleObject(gHandlerMutex, MUTEX_TIMEOUT_MS);
|
||||
if (result == WAIT_TIMEOUT) {
|
||||
LOG_ERROR_MESSAGE(L"Unable to obtain mutex ownership");
|
||||
// activitiesPerformed is already set to error. No change needed.
|
||||
} else if (result == WAIT_FAILED) {
|
||||
LOG_ERROR_MESSAGE(L"Failed to wait on mutex: %#X", GetLastError());
|
||||
// activitiesPerformed is already set to error. No change needed.
|
||||
} else if (result == WAIT_ABANDONED) {
|
||||
LOG_ERROR_MESSAGE(L"Found abandoned mutex");
|
||||
ReleaseMutex(gHandlerMutex);
|
||||
// activitiesPerformed is already set to error. No change needed.
|
||||
} else {
|
||||
// Mutex is being held. It is safe to access gHandlerReturnData.
|
||||
// If gHandlerReturnData.handlerDataHasBeenSet is false, the handler
|
||||
// never ran. Use the error value activitiesPerformed already contains.
|
||||
if (gHandlerReturnData.handlerDataHasBeenSet) {
|
||||
activitiesPerformed = gHandlerReturnData.activitiesPerformed;
|
||||
}
|
||||
|
||||
success = ReleaseMutex(gHandlerMutex);
|
||||
if (!success) {
|
||||
LOG_ERROR_MESSAGE(L"Unable to release mutex ownership: %#X",
|
||||
GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!WinToast::instance()->hideToast(id)) {
|
||||
LOG_ERROR_MESSAGE(L"Failed to hide notification");
|
||||
}
|
||||
});
|
||||
return activitiesPerformed;
|
||||
}
|
||||
|
||||
// Previously this function checked that the Firefox build was using English.
|
||||
// This was checked because of the peculiar way we were localizing toast
|
||||
// notifications where we used a completely different set of strings in English.
|
||||
//
|
||||
// We've since unified the notification flows but need to clean up unused code
|
||||
// and config files - Bug 1826375.
|
||||
bool FirefoxInstallIsEnglish() { return false; }
|
||||
|
||||
// If a notification is shown, this function will block until the notification
|
||||
// is activated or dismissed.
|
||||
// aumi is the App User Model ID.
|
||||
NotificationActivities MaybeShowNotification(
|
||||
const DefaultBrowserInfo& browserInfo, const wchar_t* aumi, bool force) {
|
||||
// Default to not showing a notification. Any other value will be returned
|
||||
// directly from ShowNotification.
|
||||
NotificationActivities activitiesPerformed = {NotificationType::Initial,
|
||||
NotificationShown::NotShown,
|
||||
NotificationAction::NoAction};
|
||||
|
||||
// Reset notification state machine, user setting default browser to Firefox
|
||||
// is a strong signal that they intend to have it as the default browser.
|
||||
if (browserInfo.currentDefaultBrowser == Browser::Firefox) {
|
||||
ResetInitialNotificationShown();
|
||||
}
|
||||
|
||||
bool initialNotificationShown = GetInitialNotificationShown();
|
||||
if (!initialNotificationShown || force) {
|
||||
if ((browserInfo.currentDefaultBrowser == Browser::EdgeWithBlink &&
|
||||
browserInfo.previousDefaultBrowser == Browser::Firefox) ||
|
||||
force) {
|
||||
return ShowNotification(NotificationType::Initial, aumi);
|
||||
}
|
||||
return activitiesPerformed;
|
||||
}
|
||||
activitiesPerformed.type = NotificationType::Followup;
|
||||
|
||||
ULONGLONG followupNotificationRequestTime =
|
||||
GetFollowupNotificationRequestTime();
|
||||
bool followupNotificationRequested = followupNotificationRequestTime != 0;
|
||||
bool followupNotificationShown = GetFollowupNotificationShown();
|
||||
if (followupNotificationRequested && !followupNotificationShown &&
|
||||
!GetFollowupNotificationSuppressed()) {
|
||||
ULONGLONG secondsSinceRequestTime =
|
||||
SecondsPassedSince(followupNotificationRequestTime);
|
||||
|
||||
if (secondsSinceRequestTime >= SEVEN_DAYS_IN_SECONDS) {
|
||||
// If we go to show the followup notification and the user has already
|
||||
// changed the default browser, permanently suppress the followup since
|
||||
// it's no longer relevant.
|
||||
if (browserInfo.currentDefaultBrowser == Browser::EdgeWithBlink) {
|
||||
return ShowNotification(NotificationType::Followup, aumi);
|
||||
} else {
|
||||
SetFollowupNotificationSuppressed(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return activitiesPerformed;
|
||||
}
|
||||
|
||||
std::string GetStringForNotificationType(NotificationType type) {
|
||||
switch (type) {
|
||||
case NotificationType::Initial:
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
#ifndef __DEFAULT_BROWSER_NOTIFICATION_H__
|
||||
#define __DEFAULT_BROWSER_NOTIFICATION_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "nsStringFwd.h"
|
||||
#include "DefaultBrowser.h"
|
||||
|
||||
namespace mozilla::default_agent {
|
||||
|
||||
|
@ -41,6 +39,9 @@ struct NotificationActivities {
|
|||
NotificationAction action;
|
||||
};
|
||||
|
||||
NotificationActivities MaybeShowNotification(
|
||||
const DefaultBrowserInfo& browserInfo, const wchar_t* aumi, bool force);
|
||||
|
||||
// These take enum values and get strings suitable for telemetry
|
||||
std::string GetStringForNotificationType(NotificationType type);
|
||||
std::string GetStringForNotificationShown(NotificationShown shown);
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
; 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 http://mozilla.org/MPL/2.0/.
|
||||
|
||||
; This file is in the UTF-8 encoding
|
||||
[Nonlocalized]
|
||||
InitialToastRelativeImagePath=browser/VisualElements/VisualElements_150.png
|
||||
FollowupToastRelativeImagePath=browser/VisualElements/VisualElements_150.png
|
||||
LocalizedToastRelativeImagePath=browser/VisualElements/VisualElements_150.png
|
|
@ -26,10 +26,20 @@ UNIFIED_SOURCES += [
|
|||
]
|
||||
|
||||
SOURCES += [
|
||||
"/third_party/WinToast/wintoastlib.cpp",
|
||||
"/toolkit/mozapps/update/common/readstrings.cpp",
|
||||
"Notification.cpp",
|
||||
]
|
||||
|
||||
# Suppress warnings from third-party code.
|
||||
SOURCES["/third_party/WinToast/wintoastlib.cpp"].flags += [
|
||||
"-Wno-implicit-fallthrough",
|
||||
"-Wno-nonportable-include-path", # Needed for wintoastlib.h including "Windows.h"
|
||||
]
|
||||
SOURCES["Notification.cpp"].flags += [
|
||||
"-Wno-nonportable-include-path", # Needed for wintoastlib.h including "Windows.h"
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
"DefaultAgent.h",
|
||||
"WindowsMutex.h",
|
||||
|
@ -42,6 +52,7 @@ USE_LIBS += [
|
|||
LOCAL_INCLUDES += [
|
||||
"/browser/components/shell/",
|
||||
"/other-licenses/nsis/Contrib/CityHash/cityhash",
|
||||
"/third_party/WinToast",
|
||||
"/toolkit/components/jsoncpp/include",
|
||||
"/toolkit/mozapps/update/common",
|
||||
]
|
||||
|
@ -87,6 +98,8 @@ for var in ("MOZ_APP_BASENAME", "MOZ_APP_DISPLAYNAME", "MOZ_APP_VENDOR"):
|
|||
DEFINES["UNICODE"] = True
|
||||
DEFINES["_UNICODE"] = True
|
||||
|
||||
FINAL_TARGET_FILES += ["defaultagent.ini"]
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
|
||||
if CONFIG["ENABLE_TESTS"]:
|
||||
|
|
|
@ -51,6 +51,20 @@ interface nsIDefaultAgent : nsISupports
|
|||
*/
|
||||
void uninstall(in AString aUniqueToken);
|
||||
|
||||
/**
|
||||
* Actually performs the default agent task, which currently means generating
|
||||
* and sending our telemetry ping and possibly showing a notification to the
|
||||
* user if their browser has switched from Firefox to Edge with Blink.
|
||||
*
|
||||
* @param {AString} aUniqueToken
|
||||
* A unique identifier for this installation; the same one provided when
|
||||
* the task was registered.
|
||||
* @param {boolean} aForce
|
||||
* For debugging, forces the task to run even if it has run in the last
|
||||
* 24 hours, and forces the notification to show.
|
||||
*/
|
||||
void doTask(in AString aUniqueToken, in boolean aForce);
|
||||
|
||||
/**
|
||||
* Checks that the main app ran recently.
|
||||
*
|
||||
|
|
Загрузка…
Ссылка в новой задаче