Global Metrics
path: .metrics.loc.sloc
old: 100.0
new: 622.0
path: .metrics.loc.lloc
old: 0.0
new: 218.0
path: .metrics.loc.ploc
old: 16.0
new: 520.0
path: .metrics.loc.cloc
old: 77.0
new: 14.0
path: .metrics.loc.blank
old: 7.0
new: 88.0
path: .metrics.halstead.length
old: 48.0
new: 2478.0
path: .metrics.halstead.vocabulary
old: 28.0
new: 342.0
path: .metrics.halstead.N2
old: 29.0
new: 1019.0
path: .metrics.halstead.bugs
old: 0.026959829530255077
new: 3.1324110985299867
path: .metrics.halstead.estimated_program_length
old: 115.65156546374811
new: 2742.6324883504067
path: .metrics.halstead.time
old: 40.40965006946971
new: 50608.9711047455
path: .metrics.halstead.effort
old: 727.3737012504548
new: 910961.479885419
path: .metrics.halstead.volume
old: 230.75303625876495
new: 20859.438531887252
path: .metrics.halstead.level
old: 0.31724137931034485
new: 0.02289826627412496
path: .metrics.halstead.n2
old: 23.0
new: 315.0
path: .metrics.halstead.N1
old: 19.0
new: 1459.0
path: .metrics.halstead.n1
old: 5.0
new: 27.0
path: .metrics.halstead.difficulty
old: 3.152173913043478
new: 43.67142857142857
path: .metrics.halstead.purity_ratio
old: 2.4094076138280855
new: 1.106792771731399
path: .metrics.cyclomatic.average
old: 1.0
new: 4.391304347826087
path: .metrics.cyclomatic.sum
old: 3.0
new: 101.0
path: .metrics.mi.mi_visual_studio
old: 39.421773814689445
new: 0.0
path: .metrics.mi.mi_original
old: 67.41123322311896
new: -8.160550925687886
path: .metrics.mi.mi_sei
old: 70.74552570694394
new: -65.673551502288
path: .metrics.nom.closures
old: 0.0
new: 5.0
path: .metrics.nom.functions
old: 0.0
new: 20.0
path: .metrics.nom.total
old: 0.0
new: 25.0
path: .metrics.nexits.average
old: null
new: 2.76
path: .metrics.nexits.sum
old: 0.0
new: 69.0
path: .metrics.nargs.sum
old: 0.0
new: 24.0
path: .metrics.nargs.average
old: null
new: 0.96
path: .metrics.cognitive.average
old: null
new: 4.32
path: .metrics.cognitive.sum
old: 0.0
new: 108.0
Spaces Data
Minimal test - lines (28, 622)
path: .spaces[0].metrics.nexits.average
old: null
new: 2.76
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 69.0
path: .spaces[0].metrics.mi.mi_sei
old: null
new: -66.86494909171078
path: .spaces[0].metrics.mi.mi_original
old: null
new: -7.125296441161609
path: .spaces[0].metrics.mi.mi_visual_studio
old: null
new: 0.0
path: .spaces[0].metrics.loc.blank
old: 0.0
new: 85.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 218.0
path: .spaces[0].metrics.loc.sloc
old: 1.0
new: 595.0
path: .spaces[0].metrics.loc.ploc
old: 1.0
new: 502.0
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 8.0
path: .spaces[0].metrics.cognitive.average
old: null
new: 4.32
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 108.0
path: .spaces[0].metrics.nom.total
old: 0.0
new: 25.0
path: .spaces[0].metrics.nom.closures
old: 0.0
new: 5.0
path: .spaces[0].metrics.nom.functions
old: 0.0
new: 20.0
path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 100.0
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 4.545454545454546
path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 24.0
path: .spaces[0].metrics.nargs.average
old: null
new: 0.96
path: .spaces[0].metrics.halstead.length
old: 1.0
new: 2460.0
path: .spaces[0].metrics.halstead.purity_ratio
old: null
new: 1.0439165615594477
path: .spaces[0].metrics.halstead.volume
old: 0.0
new: 20516.031007096175
path: .spaces[0].metrics.halstead.n2
old: 1.0
new: 297.0
path: .spaces[0].metrics.halstead.level
old: null
new: 0.02197802197802198
path: .spaces[0].metrics.halstead.N1
old: 0.0
new: 1459.0
path: .spaces[0].metrics.halstead.difficulty
old: 0.0
new: 45.5
path: .spaces[0].metrics.halstead.N2
old: 1.0
new: 1001.0
path: .spaces[0].metrics.halstead.vocabulary
old: 1.0
new: 324.0
path: .spaces[0].metrics.halstead.bugs
old: 0.0
new: 3.1838204961878684
path: .spaces[0].metrics.halstead.time
old: 0.0
new: 51859.96726793755
path: .spaces[0].metrics.halstead.n1
old: 0.0
new: 27.0
path: .spaces[0].metrics.halstead.estimated_program_length
old: null
new: 2568.0347414362413
path: .spaces[0].metrics.halstead.effort
old: 0.0
new: 933479.410822876
Code
namespace mozilla {
namespace widget {
typedef ABI::Windows::Foundation::ITypedEventHandler<
ABI::Windows::UI::Notifications::ToastNotification*, IInspectable*>
ToastActivationHandler;
typedef ABI::Windows::Foundation::ITypedEventHandler<
ABI::Windows::UI::Notifications::ToastNotification*,
ABI::Windows::UI::Notifications::ToastDismissedEventArgs*>
ToastDismissedHandler;
typedef ABI::Windows::Foundation::ITypedEventHandler<
ABI::Windows::UI::Notifications::ToastNotification*,
ABI::Windows::UI::Notifications::ToastFailedEventArgs*>
ToastFailedHandler;
using namespace ABI::Windows::Data::Xml::Dom;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::UI::Notifications;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace mozilla;
NS_IMPL_ISUPPORTS(ToastNotificationHandler, nsIAlertNotificationImageListener)
static bool SetNodeValueString(const nsString& aString, IXmlNode* node,
IXmlDocument* xml) {
ComPtr inputText;
if (NS_WARN_IF(FAILED(xml->CreateTextNode(
HStringReference(static_cast(aString.get())).Get(),
&inputText)))) {
return false;
}
ComPtr inputTextNode;
if (NS_WARN_IF(FAILED(inputText.As(&inputTextNode)))) {
return false;
}
ComPtr appendedChild;
if (NS_WARN_IF(
FAILED(node->AppendChild(inputTextNode.Get(), &appendedChild)))) {
return false;
}
return true;
}
static bool SetAttribute(IXmlElement* element, const HSTRING name,
const nsAString& value) {
HSTRING valueStr = HStringReference(static_cast(
PromiseFlatString(value).get()))
.Get();
if (NS_WARN_IF(FAILED(element->SetAttribute(name, valueStr)))) {
return false;
}
return true;
}
static bool AddActionNode(IXmlDocument* toastXml, IXmlNode* actionsNode,
const nsAString& actionTitle,
const nsAString& actionArgs) {
ComPtr action;
HRESULT hr =
toastXml->CreateElement(HStringReference(L"action").Get(), &action);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
if (NS_WARN_IF(!SetAttribute(action.Get(), HStringReference(L"content").Get(),
actionTitle))) {
return false;
}
if (NS_WARN_IF(!SetAttribute(
action.Get(), HStringReference(L"arguments").Get(), actionArgs))) {
return false;
}
if (NS_WARN_IF(!SetAttribute(action.Get(),
HStringReference(L"placement").Get(),
u"contextmenu"_ns))) {
return false;
}
// Add to
ComPtr actionNode;
hr = action.As(&actionNode);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
ComPtr appendedChild;
hr = actionsNode->AppendChild(actionNode.Get(), &appendedChild);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
return true;
}
static ComPtr
GetToastNotificationManagerStatics() {
ComPtr toastNotificationManagerStatics;
if (NS_WARN_IF(FAILED(GetActivationFactory(
HStringReference(
RuntimeClass_Windows_UI_Notifications_ToastNotificationManager)
.Get(),
&toastNotificationManagerStatics)))) {
return nullptr;
}
return toastNotificationManagerStatics;
}
ToastNotificationHandler::~ToastNotificationHandler() {
if (mImageRequest) {
mImageRequest->Cancel(NS_BINDING_ABORTED);
mImageRequest = nullptr;
}
if (mHasImage) {
DebugOnly rv = mImageFile->Remove(false);
NS_ASSERTION(NS_SUCCEEDED(rv), "Cannot remove temporary image file");
}
UnregisterHandler();
}
void ToastNotificationHandler::UnregisterHandler() {
if (mNotification && mNotifier) {
mNotification->remove_Dismissed(mDismissedToken);
mNotification->remove_Activated(mActivatedToken);
mNotification->remove_Failed(mFailedToken);
mNotifier->Hide(mNotification.Get());
}
mNotification = nullptr;
mNotifier = nullptr;
SendFinished();
}
ComPtr ToastNotificationHandler::InitializeXmlForTemplate(
ToastTemplateType templateType) {
ComPtr toastNotificationManagerStatics =
GetToastNotificationManagerStatics();
ComPtr toastXml;
toastNotificationManagerStatics->GetTemplateContent(templateType, &toastXml);
return toastXml;
}
nsresult ToastNotificationHandler::InitAlertAsync(
nsIAlertNotification* aAlert) {
return aAlert->LoadImage(/* aTimeout = */ 0, this, /* aUserData = */ nullptr,
getter_AddRefs(mImageRequest));
}
bool ToastNotificationHandler::ShowAlert() {
if (!mBackend->IsActiveHandler(mName, this)) {
return true;
}
ToastTemplateType toastTemplate;
if (mHostPort.IsEmpty()) {
toastTemplate =
mHasImage ? ToastTemplateType::ToastTemplateType_ToastImageAndText03
: ToastTemplateType::ToastTemplateType_ToastText03;
} else {
toastTemplate =
mHasImage ? ToastTemplateType::ToastTemplateType_ToastImageAndText04
: ToastTemplateType::ToastTemplateType_ToastText04;
}
ComPtr toastXml = InitializeXmlForTemplate(toastTemplate);
if (!toastXml) {
return false;
}
HRESULT hr;
if (mHasImage) {
ComPtr toastImageElements;
hr = toastXml->GetElementsByTagName(HStringReference(L"image").Get(),
&toastImageElements);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
ComPtr imageNode;
hr = toastImageElements->Item(0, &imageNode);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
ComPtr image;
hr = imageNode.As(&image);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
if (NS_WARN_IF(!SetAttribute(image.Get(), HStringReference(L"src").Get(),
mImageUri))) {
return false;
}
}
ComPtr toastTextElements;
hr = toastXml->GetElementsByTagName(HStringReference(L"text").Get(),
&toastTextElements);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
ComPtr titleTextNodeRoot;
hr = toastTextElements->Item(0, &titleTextNodeRoot);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
ComPtr msgTextNodeRoot;
hr = toastTextElements->Item(1, &msgTextNodeRoot);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
if (NS_WARN_IF(!SetNodeValueString(mTitle, titleTextNodeRoot.Get(),
toastXml.Get()))) {
return false;
}
if (NS_WARN_IF(
!SetNodeValueString(mMsg, msgTextNodeRoot.Get(), toastXml.Get()))) {
return false;
}
ComPtr toastElements;
hr = toastXml->GetElementsByTagName(HStringReference(L"toast").Get(),
&toastElements);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
ComPtr toastNodeRoot;
hr = toastElements->Item(0, &toastNodeRoot);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
ComPtr actions;
hr = toastXml->CreateElement(HStringReference(L"actions").Get(), &actions);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
ComPtr actionsNode;
hr = actions.As(&actionsNode);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
nsCOMPtr sbs =
do_GetService(NS_STRINGBUNDLE_CONTRACTID);
if (NS_WARN_IF(!sbs)) {
return false;
}
nsCOMPtr bundle;
sbs->CreateBundle("chrome://alerts/locale/alert.properties",
getter_AddRefs(bundle));
if (NS_WARN_IF(!bundle)) {
return false;
}
if (!mHostPort.IsEmpty()) {
AutoTArray formatStrings = {mHostPort};
ComPtr urlTextNodeRoot;
hr = toastTextElements->Item(2, &urlTextNodeRoot);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
nsAutoString urlReference;
bundle->FormatStringFromName("source.label", formatStrings, urlReference);
if (NS_WARN_IF(!SetNodeValueString(urlReference, urlTextNodeRoot.Get(),
toastXml.Get()))) {
return false;
}
if (IsWin10AnniversaryUpdateOrLater()) {
ComPtr placementText;
hr = urlTextNodeRoot.As(&placementText);
if (SUCCEEDED(hr)) {
// placement is supported on Windows 10 Anniversary Update or later
SetAttribute(placementText.Get(), HStringReference(L"placement").Get(),
u"attribution"_ns);
}
}
nsAutoString disableButtonTitle;
bundle->FormatStringFromName("webActions.disableForOrigin.label",
formatStrings, disableButtonTitle);
AddActionNode(toastXml.Get(), actionsNode.Get(), disableButtonTitle,
u"snooze"_ns);
}
nsAutoString settingsButtonTitle;
bundle->GetStringFromName("webActions.settings.label", settingsButtonTitle);
AddActionNode(toastXml.Get(), actionsNode.Get(), settingsButtonTitle,
u"settings"_ns);
ComPtr appendedChild;
hr = toastNodeRoot->AppendChild(actionsNode.Get(), &appendedChild);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
return CreateWindowsNotificationFromXml(toastXml.Get());
}
bool ToastNotificationHandler::CreateWindowsNotificationFromXml(
IXmlDocument* aXml) {
ComPtr factory;
HRESULT hr = GetActivationFactory(
HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotification)
.Get(),
&factory);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
hr = factory->CreateToastNotification(aXml, &mNotification);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
RefPtr self = this;
hr = mNotification->add_Activated(
Callback([self](IToastNotification* aNotification,
IInspectable* aInspectable) {
return self->OnActivate(aNotification, aInspectable);
}).Get(),
&mActivatedToken);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
hr = mNotification->add_Dismissed(
Callback([self](IToastNotification* aNotification,
IToastDismissedEventArgs* aArgs) {
return self->OnDismiss(aNotification, aArgs);
}).Get(),
&mDismissedToken);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
hr = mNotification->add_Failed(
Callback([self](IToastNotification* aNotification,
IToastFailedEventArgs* aArgs) {
return self->OnFail(aNotification, aArgs);
}).Get(),
&mFailedToken);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
ComPtr toastNotificationManagerStatics =
GetToastNotificationManagerStatics();
if (NS_WARN_IF(!toastNotificationManagerStatics)) {
return false;
}
nsAutoString uid;
if (NS_WARN_IF(!WinTaskbar::GetAppUserModelID(uid))) {
return false;
}
HSTRING uidStr =
HStringReference(static_cast(uid.get())).Get();
hr = toastNotificationManagerStatics->CreateToastNotifierWithId(uidStr,
&mNotifier);
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
hr = mNotifier->Show(mNotification.Get());
if (NS_WARN_IF(FAILED(hr))) {
return false;
}
if (mAlertListener) {
mAlertListener->Observe(nullptr, "alertshow", mCookie.get());
}
return true;
}
void ToastNotificationHandler::SendFinished() {
if (!mSentFinished && mAlertListener) {
mAlertListener->Observe(nullptr, "alertfinished", mCookie.get());
}
mSentFinished = true;
}
HRESULT
ToastNotificationHandler::OnActivate(IToastNotification* notification,
IInspectable* inspectable) {
if (mAlertListener) {
nsAutoString argString;
if (inspectable) {
ComPtr eventArgs;
HRESULT hr = inspectable->QueryInterface(
__uuidof(IToastActivatedEventArgs), (void**)&eventArgs);
if (SUCCEEDED(hr)) {
HSTRING arguments;
hr = eventArgs->get_Arguments(&arguments);
if (SUCCEEDED(hr)) {
uint32_t len = 0;
const wchar_t* buffer = WindowsGetStringRawBuffer(arguments, &len);
if (buffer) {
argString.Assign(buffer, len);
}
}
}
}
if (argString.EqualsLiteral("settings")) {
mAlertListener->Observe(nullptr, "alertsettingscallback", mCookie.get());
} else if (argString.EqualsLiteral("snooze")) {
mAlertListener->Observe(nullptr, "alertdisablecallback", mCookie.get());
} else if (mClickable) {
// When clicking toast, focus moves to another process, but we want to set
// focus on Firefox process.
nsCOMPtr winMediator(
do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
if (winMediator) {
nsCOMPtr navWin;
winMediator->GetMostRecentWindow(u"navigator:browser",
getter_AddRefs(navWin));
if (navWin) {
nsCOMPtr widget =
WidgetUtils::DOMWindowToWidget(nsPIDOMWindowOuter::From(navWin));
if (widget) {
SetForegroundWindow(
static_cast(widget->GetNativeData(NS_NATIVE_WINDOW)));
}
}
}
mAlertListener->Observe(nullptr, "alertclickcallback", mCookie.get());
}
}
mBackend->RemoveHandler(mName, this);
return S_OK;
}
HRESULT
ToastNotificationHandler::OnDismiss(IToastNotification* notification,
IToastDismissedEventArgs* aArgs) {
SendFinished();
mBackend->RemoveHandler(mName, this);
return S_OK;
}
HRESULT
ToastNotificationHandler::OnFail(IToastNotification* notification,
IToastFailedEventArgs* aArgs) {
SendFinished();
mBackend->RemoveHandler(mName, this);
return S_OK;
}
nsresult ToastNotificationHandler::TryShowAlert() {
if (NS_WARN_IF(!ShowAlert())) {
mBackend->RemoveHandler(mName, this);
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
ToastNotificationHandler::OnImageMissing(nsISupports*) {
return TryShowAlert();
}
NS_IMETHODIMP
ToastNotificationHandler::OnImageReady(nsISupports*, imgIRequest* aRequest) {
nsresult rv = AsyncSaveImage(aRequest);
if (NS_FAILED(rv)) {
return TryShowAlert();
}
return rv;
}
nsresult ToastNotificationHandler::AsyncSaveImage(imgIRequest* aRequest) {
nsresult rv =
NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mImageFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mImageFile->Append(u"notificationimages"_ns);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mImageFile->Create(nsIFile::DIRECTORY_TYPE, 0500);
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
return rv;
}
nsCOMPtr idGen =
do_GetService("@mozilla.org/uuid-generator;1", &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsID uuid;
rv = idGen->GenerateUUIDInPlace(&uuid);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
char uuidChars[NSID_LENGTH];
uuid.ToProvidedString(uuidChars);
// Remove the brackets at the beginning and ending of the generated UUID.
nsAutoCString uuidStr(Substring(uuidChars + 1, uuidChars + NSID_LENGTH - 2));
uuidStr.AppendLiteral(".bmp");
mImageFile->AppendNative(uuidStr);
nsCOMPtr imgContainer;
rv = aRequest->GetImage(getter_AddRefs(imgContainer));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsMainThreadPtrHandle self(
new nsMainThreadPtrHolder(
"ToastNotificationHandler", this));
nsCOMPtr imageFile(mImageFile);
RefPtr surface = imgContainer->GetFrame(
imgIContainer::FRAME_FIRST,
imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
nsCOMPtr r = NS_NewRunnableFunction(
"ToastNotificationHandler::AsyncWriteBitmap",
[self, imageFile, surface]() -> void {
nsresult rv;
if (!surface) {
rv = NS_ERROR_FAILURE;
} else {
rv = WinUtils::WriteBitmap(imageFile, surface);
}
nsCOMPtr cbRunnable = NS_NewRunnableFunction(
"ToastNotificationHandler::AsyncWriteBitmapCb",
[self, rv]() -> void {
auto handler = const_cast(self.get());
handler->OnWriteBitmapFinished(rv);
});
NS_DispatchToMainThread(cbRunnable);
});
return mBackend->BackgroundDispatch(r);
}
void ToastNotificationHandler::OnWriteBitmapFinished(nsresult rv) {
if (NS_SUCCEEDED(rv)) {
OnWriteBitmapSuccess();
}
TryShowAlert();
}
nsresult ToastNotificationHandler::OnWriteBitmapSuccess() {
nsresult rv;
nsCOMPtr fileURI;
rv = NS_NewFileURI(getter_AddRefs(fileURI), mImageFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoCString uriStr;
rv = fileURI->GetSpec(uriStr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
AppendUTF8toUTF16(uriStr, mImageUri);
mHasImage = true;
return NS_OK;
}
} // namespace widget
} // namespace mozilla