diff options
Diffstat (limited to 'toolkit/components/aboutwindowsmessages')
11 files changed, 400 insertions, 0 deletions
diff --git a/toolkit/components/aboutwindowsmessages/AboutWindowsMessages.cpp b/toolkit/components/aboutwindowsmessages/AboutWindowsMessages.cpp new file mode 100644 index 0000000000..86308dfe13 --- /dev/null +++ b/toolkit/components/aboutwindowsmessages/AboutWindowsMessages.cpp @@ -0,0 +1,93 @@ +/* -*- 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 http://mozilla.org/MPL/2.0/. */ + +#include "AboutWindowsMessages.h" + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/StaticPtr.h" +#include "nsThreadUtils.h" +#include "nsPIDOMWindow.h" +#include "nsServiceManagerUtils.h" +#include "nsIWindowMediator.h" +#include "nsIAppWindow.h" +#include "nsIBaseWindow.h" +#include "nsIDocShell.h" +#include "nsDocShellTreeOwner.h" +#include "mozilla/WidgetUtils.h" +#include "mozilla/widget/nsWindowLoggedMessages.h" + +namespace mozilla { + +static StaticRefPtr<AboutWindowsMessages> sSingleton; + +NS_IMPL_ISUPPORTS(AboutWindowsMessages, nsIAboutWindowsMessages); + +/*static*/ +already_AddRefed<AboutWindowsMessages> AboutWindowsMessages::GetSingleton() { + if (!sSingleton) { + sSingleton = new AboutWindowsMessages; + ClearOnShutdown(&sSingleton); + } + + return do_AddRef(sSingleton); +} + +static bool GetWindowTitleFromWindow(const nsCOMPtr<nsPIDOMWindowOuter>& window, + nsAutoString& title) { + nsIDocShell* docShell = window->GetDocShell(); + if (docShell) { + nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner; + docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner)); + nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(parentTreeOwner)); + baseWindow->GetTitle(title); + return true; + } + return false; +} + +NS_IMETHODIMP +AboutWindowsMessages::GetMessages(mozIDOMWindowProxy* currentWindow, + nsTArray<nsTArray<nsCString>>& messages, + nsTArray<nsString>& windowTitles) { + messages.Clear(); + // Display the current window's messages first + auto currentWindowOuter = nsPIDOMWindowOuter::From(currentWindow); + RefPtr<nsIWidget> currentWidget = + widget::WidgetUtils::DOMWindowToWidget(currentWindowOuter); + nsAutoString currentWindowTitle; + if (GetWindowTitleFromWindow(currentWindowOuter, currentWindowTitle)) { + nsTArray<nsCString> windowMessages; + widget::GetLatestWindowMessages(currentWidget, windowMessages); + windowTitles.AppendElement(currentWindowTitle); + messages.EmplaceBack(std::move(windowMessages)); + } + nsCOMPtr<nsIWindowMediator> mediator( + do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); + nsCOMPtr<nsISimpleEnumerator> windowEnumerator; + mediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator)); + if (!windowEnumerator) return NS_ERROR_FAILURE; + bool more; + while (NS_SUCCEEDED(windowEnumerator->HasMoreElements(&more)) && more) { + nsCOMPtr<nsISupports> isupports; + if (NS_FAILED(windowEnumerator->GetNext(getter_AddRefs(isupports)))) break; + nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(isupports); + NS_ASSERTION(window, "not an nsPIDOMWindowOuter"); + RefPtr<nsIWidget> windowWidget = + widget::WidgetUtils::DOMWindowToWidget(window); + if (&*windowWidget != &*currentWidget) { + nsAutoString title; + if (GetWindowTitleFromWindow(window, title)) { + nsTArray<nsCString> windowMessages; + widget::GetLatestWindowMessages(windowWidget, windowMessages); + windowTitles.AppendElement(title); + messages.EmplaceBack(std::move(windowMessages)); + } + } + } + return NS_OK; +} +} // namespace mozilla diff --git a/toolkit/components/aboutwindowsmessages/AboutWindowsMessages.h b/toolkit/components/aboutwindowsmessages/AboutWindowsMessages.h new file mode 100644 index 0000000000..c236bbcd30 --- /dev/null +++ b/toolkit/components/aboutwindowsmessages/AboutWindowsMessages.h @@ -0,0 +1,29 @@ +/* -*- 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 http://mozilla.org/MPL/2.0/. */ + +#ifndef __AboutWindowsMessages_h__ +#define __AboutWindowsMessages_h__ + +#include "mozilla/MozPromise.h" +#include "nsIAboutWindowsMessages.h" + +namespace mozilla { + +class AboutWindowsMessages final : public nsIAboutWindowsMessages { + ~AboutWindowsMessages() = default; + + public: + static already_AddRefed<AboutWindowsMessages> GetSingleton(); + + AboutWindowsMessages() = default; + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIABOUTWINDOWSMESSAGES +}; + +} // namespace mozilla + +#endif // __AboutWindowsMessages_h__ diff --git a/toolkit/components/aboutwindowsmessages/components.conf b/toolkit/components/aboutwindowsmessages/components.conf new file mode 100644 index 0000000000..52e9b12abe --- /dev/null +++ b/toolkit/components/aboutwindowsmessages/components.conf @@ -0,0 +1,17 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+ {
+ 'cid': '{ba8cdb8a-9624-493d-aac4-23ee83378d7c}',
+ 'contract_ids': ['@mozilla.org/about-windowsmessages;1'],
+ 'type': 'AboutWindowsMessages',
+ 'singleton': True,
+ 'constructor': 'mozilla::AboutWindowsMessages::GetSingleton',
+ 'headers': ['mozilla/AboutWindowsMessages.h'],
+ 'processes': ProcessSelector.MAIN_PROCESS_ONLY,
+ },
+]
\ No newline at end of file diff --git a/toolkit/components/aboutwindowsmessages/content/aboutWindowsMessages.css b/toolkit/components/aboutwindowsmessages/content/aboutWindowsMessages.css new file mode 100644 index 0000000000..de29761cc6 --- /dev/null +++ b/toolkit/components/aboutwindowsmessages/content/aboutWindowsMessages.css @@ -0,0 +1,15 @@ +/* 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/. */ + +.card { + margin-bottom: 16px; +} + +.window-card-title { + display: inline; +} + +.window-card-title:not(.current-window) { + font-weight: normal; +} diff --git a/toolkit/components/aboutwindowsmessages/content/aboutWindowsMessages.html b/toolkit/components/aboutwindowsmessages/content/aboutWindowsMessages.html new file mode 100644 index 0000000000..5c3145baf2 --- /dev/null +++ b/toolkit/components/aboutwindowsmessages/content/aboutWindowsMessages.html @@ -0,0 +1,42 @@ +<!-- 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/. --> + +<!DOCTYPE html> + +<html> + <head> + <title data-l10n-id="windows-messages-page-title"></title> + <meta + http-equiv="Content-Security-Policy" + content="default-src chrome:; object-src 'none'" + /> + <meta name="color-scheme" content="light dark" /> + <link + rel="stylesheet" + href="chrome://global/skin/in-content/info-pages.css" + /> + <link + rel="stylesheet" + href="chrome://global/content/aboutWindowsMessages.css" + /> + <link rel="localization" href="branding/brand.ftl" /> + <link rel="localization" href="toolkit/about/aboutWindowsMessages.ftl" /> + <script src="chrome://global/content/aboutWindowsMessages.js"></script> + </head> + + <body class="wide-container"> + <h1 data-l10n-id="windows-messages-page-title"></h1> + <p data-l10n-id="windows-messages-intro"></p> + <div id="windows-div"></div> + + <template name="window-card"> + <details class="card"> + <summary> + <h3 class="window-card-title"></h3> + <button data-l10n-id="windows-messages-copy-to-clipboard"></button> + </summary> + </details> + </template> + </body> +</html> diff --git a/toolkit/components/aboutwindowsmessages/content/aboutWindowsMessages.js b/toolkit/components/aboutwindowsmessages/content/aboutWindowsMessages.js new file mode 100644 index 0000000000..0896ae7669 --- /dev/null +++ b/toolkit/components/aboutwindowsmessages/content/aboutWindowsMessages.js @@ -0,0 +1,70 @@ +/* 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/. */ + +"use strict"; + +let AboutWindowsMessages = null; + +function refreshMessages() { + let windowMessages = {}; + let windowTitles = {}; + AboutWindowsMessages.getMessages(window, windowMessages, windowTitles); + let windowsDiv = document.getElementById("windows-div"); + windowsDiv.innerHTML = ""; + const templateCard = document.querySelector("template[name=window-card]"); + for (let i = 0; i < windowTitles.value.length; ++i) { + let windowCard = templateCard.content + .cloneNode(true) + .querySelector("details"); + // open the current window by default + windowCard.open = i === 0; + let summary = windowCard.querySelector("summary"); + let titleSpan = summary.querySelector("h3.window-card-title"); + titleSpan.appendChild(document.createTextNode(windowTitles.value[i])); + titleSpan.classList.toggle("current-window", windowCard.open); + let copyButton = summary.querySelector("button"); + copyButton.addEventListener("click", async e => { + e.target.disabled = true; + await copyMessagesToClipboard(e); + e.target.disabled = false; + }); + let innerUl = document.createElement("ul"); + for (let j = 0; j < windowMessages.value[i].length; ++j) { + let innerLi = document.createElement("li"); + innerLi.className = "message"; + innerLi.innerText = windowMessages.value[i][j]; + innerUl.appendChild(innerLi); + } + windowCard.appendChild(innerUl); + windowsDiv.append(windowCard); + } +} + +async function copyMessagesToClipboard(event) { + const details = event.target.parentElement.parentElement; + // Avoid copying the window name as it is Category 3 data, + // and only useful for the user to identify which window + // is which. + const messagesText = + Array.from(details.querySelector("ul").children) + .map(li => li.innerText) + .join("\n") + "\n"; + + await navigator.clipboard.writeText(messagesText); +} + +function onLoad() { + refreshMessages(); +} + +try { + AboutWindowsMessages = Cc["@mozilla.org/about-windowsmessages;1"].getService( + Ci.nsIAboutWindowsMessages + ); + document.addEventListener("DOMContentLoaded", onLoad, { once: true }); +} catch (ex) { + // Do nothing if we fail to create a singleton instance, + // showing the default no-module message. + console.error(ex); +} diff --git a/toolkit/components/aboutwindowsmessages/jar.mn b/toolkit/components/aboutwindowsmessages/jar.mn new file mode 100644 index 0000000000..c1d4f2b4f7 --- /dev/null +++ b/toolkit/components/aboutwindowsmessages/jar.mn @@ -0,0 +1,8 @@ +# 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/. + +toolkit.jar: + content/global/aboutWindowsMessages.css (content/aboutWindowsMessages.css) + content/global/aboutWindowsMessages.html (content/aboutWindowsMessages.html) + content/global/aboutWindowsMessages.js (content/aboutWindowsMessages.js) diff --git a/toolkit/components/aboutwindowsmessages/moz.build b/toolkit/components/aboutwindowsmessages/moz.build new file mode 100644 index 0000000000..5827cca885 --- /dev/null +++ b/toolkit/components/aboutwindowsmessages/moz.build @@ -0,0 +1,24 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +with Files("**"): + BUG_COMPONENT = ("Core", "Widget: Win32") + +FINAL_LIBRARY = "xul" + +BROWSER_CHROME_MANIFESTS += ["tests/browser/browser.toml"] +JAR_MANIFESTS += ["jar.mn"] +XPCOM_MANIFESTS += ["components.conf"] +XPIDL_MODULE = "AboutWindowsMessages" +XPIDL_SOURCES += ["nsIAboutWindowsMessages.idl"] + +EXPORTS.mozilla += [ + "AboutWindowsMessages.h", +] + +SOURCES += [ + "AboutWindowsMessages.cpp", +] diff --git a/toolkit/components/aboutwindowsmessages/nsIAboutWindowsMessages.idl b/toolkit/components/aboutwindowsmessages/nsIAboutWindowsMessages.idl new file mode 100644 index 0000000000..d240a9233a --- /dev/null +++ b/toolkit/components/aboutwindowsmessages/nsIAboutWindowsMessages.idl @@ -0,0 +1,14 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* 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/. */ + +#include "nsISupports.idl" + +interface mozIDOMWindowProxy; + +[scriptable, uuid(3c5cfbfd-545c-4910-a34a-203063914f96)] +interface nsIAboutWindowsMessages : nsISupports +{ + void getMessages(in mozIDOMWindowProxy currentWindow, out Array<Array<ACString> > messages, out Array<AString> windowTitles); +}; diff --git a/toolkit/components/aboutwindowsmessages/tests/browser/browser.toml b/toolkit/components/aboutwindowsmessages/tests/browser/browser.toml new file mode 100644 index 0000000000..650fabf228 --- /dev/null +++ b/toolkit/components/aboutwindowsmessages/tests/browser/browser.toml @@ -0,0 +1,3 @@ +[DEFAULT] + +["browser_aboutwindowsmessages.js"] diff --git a/toolkit/components/aboutwindowsmessages/tests/browser/browser_aboutwindowsmessages.js b/toolkit/components/aboutwindowsmessages/tests/browser/browser_aboutwindowsmessages.js new file mode 100644 index 0000000000..7c667891d6 --- /dev/null +++ b/toolkit/components/aboutwindowsmessages/tests/browser/browser_aboutwindowsmessages.js @@ -0,0 +1,85 @@ +/* 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/. */ + +async function resizeWindow(windowToResize, width, height) { + let resizePromise = BrowserTestUtils.waitForEvent( + windowToResize, + "resize", + false + ); + windowToResize.resizeTo(width, height); + await resizePromise; +} + +add_task(async () => { + const TEST_LINK = "https://example.com/"; + let originalBrowserWindow = + Services.wm.getMostRecentWindow("navigator:browser"); + let originalBrowser = originalBrowserWindow.gBrowser; + // Resize this window so we can check for WM_NCCALCSIZE events below + await resizeWindow(originalBrowserWindow, 500, 400); + await resizeWindow(originalBrowserWindow, 600, 500); + await resizeWindow(originalBrowserWindow, 700, 600); + let newWindow = await BrowserTestUtils.openNewBrowserWindow({ + url: TEST_LINK, + }); + registerCleanupFunction(async function () { + await BrowserTestUtils.closeWindow(newWindow); + }); + await BrowserTestUtils.withNewTab( + { gBrowser: originalBrowser, url: "about:windows-messages" }, + async browser => { + let messagesList = content.document.getElementById("windows-div"); + // This is tricky because the test framework has its own windows + Assert.greaterOrEqual( + messagesList.childNodes.length, + 2, + "should have enough window entries" + ); + + let firstList = true; + for (let sublist of messagesList.childNodes) { + const messages = Array.from(sublist.querySelectorAll("li.message")); + const numberOfMessages = messages.length; + // Every window gets a few window messages when it opens, so there + // should be at least a few here. + Assert.greaterOrEqual( + numberOfMessages, + 3, + "should have enough messages for the current window" + ); + if (firstList) { + // It would be nice if we could check for a less obscure event. + // However, since we're resizing the window programatically we don't + // get WM_SIZE or WM_SIZING events. + Assert.greaterOrEqual( + messages.filter(messageLi => + messageLi.innerHTML.includes("WM_NCCALCSIZE") + ).length, + 5, + "active window should have enough WM_NCCALCSIZE events" + ); + firstList = false; + } + let buttons = sublist.querySelectorAll("button"); + Assert.equal(buttons.length, 1, "should have only one button"); + let clipboardButton = buttons[0]; + clipboardButton.click(); + // Wait until copying is done and the button becomes clickable. + await BrowserTestUtils.waitForMutationCondition( + clipboardButton, + { attributes: true }, + () => !clipboardButton.disabled + ); + const clipboardText = await navigator.clipboard.readText(); + const numberOfLines = Array.from(clipboardText.matchAll("\n")).length; + Assert.equal( + numberOfLines, + numberOfMessages, + "should copy the right number of lines to the clipboard" + ); + } + } + ); +}); |