diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/xul/ChromeObserver.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/xul/ChromeObserver.cpp')
-rw-r--r-- | dom/xul/ChromeObserver.cpp | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/dom/xul/ChromeObserver.cpp b/dom/xul/ChromeObserver.cpp new file mode 100644 index 0000000000..37eeb261f6 --- /dev/null +++ b/dom/xul/ChromeObserver.cpp @@ -0,0 +1,262 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "ChromeObserver.h" + +#include "nsIBaseWindow.h" +#include "nsIWidget.h" +#include "nsIFrame.h" + +#include "nsContentUtils.h" +#include "nsView.h" +#include "nsPresContext.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/DocumentInlines.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/MutationEventBinding.h" +#include "nsXULElement.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_ISUPPORTS(ChromeObserver, nsIMutationObserver) + +ChromeObserver::ChromeObserver(Document* aDocument) + : nsStubMutationObserver(), mDocument(aDocument) {} + +ChromeObserver::~ChromeObserver() = default; + +void ChromeObserver::Init() { + mDocument->AddMutationObserver(this); + Element* rootElement = mDocument->GetRootElement(); + if (!rootElement) { + return; + } + nsAutoScriptBlocker scriptBlocker; + uint32_t attributeCount = rootElement->GetAttrCount(); + for (uint32_t i = 0; i < attributeCount; i++) { + BorrowedAttrInfo info = rootElement->GetAttrInfoAt(i); + const nsAttrName* name = info.mName; + if (name->LocalName() == nsGkAtoms::chromemargin) { + // Some linux windows managers have an issue when the chrome margin is + // applied while the browser is loading (bug 1598848). For now, skip + // applying this attribute when initializing. + continue; + } + AttributeChanged(rootElement, name->NamespaceID(), name->LocalName(), + MutationEvent_Binding::ADDITION, nullptr); + } +} + +nsIWidget* ChromeObserver::GetWindowWidget() { + // only top level chrome documents can set the titlebar color + if (mDocument && mDocument->IsRootDisplayDocument()) { + nsCOMPtr<nsISupports> container = mDocument->GetContainer(); + nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container); + if (baseWindow) { + nsCOMPtr<nsIWidget> mainWidget; + baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); + return mainWidget; + } + } + return nullptr; +} + +class SetDrawInTitleBarEvent : public Runnable { + public: + SetDrawInTitleBarEvent(nsIWidget* aWidget, bool aState) + : mozilla::Runnable("SetDrawInTitleBarEvent"), + mWidget(aWidget), + mState(aState) {} + + NS_IMETHOD Run() override { + NS_ASSERTION(mWidget, + "You shouldn't call this runnable with a null widget!"); + + mWidget->SetDrawsInTitlebar(mState); + return NS_OK; + } + + private: + nsCOMPtr<nsIWidget> mWidget; + bool mState; +}; + +void ChromeObserver::SetDrawsInTitlebar(bool aState) { + nsIWidget* mainWidget = GetWindowWidget(); + if (mainWidget) { + nsContentUtils::AddScriptRunner( + new SetDrawInTitleBarEvent(mainWidget, aState)); + } +} + +void ChromeObserver::SetDrawsTitle(bool aState) { + nsIWidget* mainWidget = GetWindowWidget(); + if (mainWidget) { + // We can do this synchronously because SetDrawsTitle doesn't have any + // synchronous effects apart from a harmless invalidation. + mainWidget->SetDrawsTitle(aState); + } +} + +void ChromeObserver::UpdateBrightTitlebarForeground() { + nsIWidget* mainWidget = GetWindowWidget(); + if (mainWidget) { + // We can do this synchronously because SetBrightTitlebarForeground doesn't + // have any synchronous effects apart from a harmless invalidation. + mainWidget->SetUseBrightTitlebarForeground( + mDocument->GetDocumentLWTheme() == Document::Doc_Theme_Bright || + mDocument->GetRootElement()->AttrValueIs( + kNameSpaceID_None, nsGkAtoms::brighttitlebarforeground, u"true"_ns, + eCaseMatters)); + } +} + +class MarginSetter : public Runnable { + public: + explicit MarginSetter(nsIWidget* aWidget) + : mozilla::Runnable("MarginSetter"), + mWidget(aWidget), + mMargin(-1, -1, -1, -1) {} + MarginSetter(nsIWidget* aWidget, const LayoutDeviceIntMargin& aMargin) + : mozilla::Runnable("MarginSetter"), mWidget(aWidget), mMargin(aMargin) {} + + NS_IMETHOD Run() override { + // SetNonClientMargins can dispatch native events, hence doing + // it off a script runner. + mWidget->SetNonClientMargins(mMargin); + return NS_OK; + } + + private: + nsCOMPtr<nsIWidget> mWidget; + LayoutDeviceIntMargin mMargin; +}; + +void ChromeObserver::SetChromeMargins(const nsAttrValue* aValue) { + if (!aValue) return; + + nsIWidget* mainWidget = GetWindowWidget(); + if (!mainWidget) return; + + // top, right, bottom, left - see nsAttrValue + nsIntMargin margins; + bool gotMargins = false; + + if (aValue->Type() == nsAttrValue::eIntMarginValue) { + gotMargins = aValue->GetIntMarginValue(margins); + } else { + nsAutoString tmp; + aValue->ToString(tmp); + gotMargins = nsContentUtils::ParseIntMarginValue(tmp, margins); + } + if (gotMargins) { + nsContentUtils::AddScriptRunner(new MarginSetter( + mainWidget, LayoutDeviceIntMargin::FromUnknownMargin(margins))); + } +} + +void ChromeObserver::AttributeChanged(dom::Element* aElement, + int32_t aNamespaceID, nsAtom* aName, + int32_t aModType, + const nsAttrValue* aOldValue) { + // We only care about changes to the root element. + if (!mDocument || aElement != mDocument->GetRootElement()) { + return; + } + + const nsAttrValue* value = aElement->GetParsedAttr(aName, aNamespaceID); + if (value) { + // Hide chrome if needed + if (aName == nsGkAtoms::hidechrome) { + HideWindowChrome(value->Equals(u"true"_ns, eCaseMatters)); + } else if (aName == nsGkAtoms::chromemargin) { + SetChromeMargins(value); + } else if (aName == nsGkAtoms::windowtype && aElement->IsXULElement()) { + RefPtr<nsXULElement> xulElement = nsXULElement::FromNodeOrNull(aElement); + xulElement->MaybeUpdatePrivateLifetime(); + } + // title and drawintitlebar are settable on + // any root node (windows, dialogs, etc) + else if (aName == nsGkAtoms::title) { + mDocument->NotifyPossibleTitleChange(false); + } else if (aName == nsGkAtoms::drawintitlebar) { + SetDrawsInTitlebar(value->Equals(u"true"_ns, eCaseMatters)); + } else if (aName == nsGkAtoms::drawtitle) { + SetDrawsTitle(value->Equals(u"true"_ns, eCaseMatters)); + } else if (aName == nsGkAtoms::localedir) { + // if the localedir changed on the root element, reset the document + // direction + mDocument->ResetDocumentDirection(); + } else if (aName == nsGkAtoms::lwtheme || + aName == nsGkAtoms::lwthemetextcolor) { + // if the lwtheme changed, make sure to reset the document lwtheme + // cache + mDocument->ResetDocumentLWTheme(); + UpdateBrightTitlebarForeground(); + } else if (aName == nsGkAtoms::brighttitlebarforeground) { + UpdateBrightTitlebarForeground(); + } + } else { + if (aName == nsGkAtoms::hidechrome) { + HideWindowChrome(false); + } else if (aName == nsGkAtoms::chromemargin) { + ResetChromeMargins(); + } else if (aName == nsGkAtoms::localedir) { + // if the localedir changed on the root element, reset the document + // direction + mDocument->ResetDocumentDirection(); + } else if ((aName == nsGkAtoms::lwtheme || + aName == nsGkAtoms::lwthemetextcolor)) { + // if the lwtheme changed, make sure to restyle appropriately + mDocument->ResetDocumentLWTheme(); + UpdateBrightTitlebarForeground(); + } else if (aName == nsGkAtoms::brighttitlebarforeground) { + UpdateBrightTitlebarForeground(); + } else if (aName == nsGkAtoms::drawintitlebar) { + SetDrawsInTitlebar(false); + } else if (aName == nsGkAtoms::drawtitle) { + SetDrawsTitle(false); + } + } +} + +void ChromeObserver::NodeWillBeDestroyed(const nsINode* aNode) { + mDocument = nullptr; +} + +void ChromeObserver::ResetChromeMargins() { + nsIWidget* mainWidget = GetWindowWidget(); + if (!mainWidget) return; + // See nsIWidget + nsContentUtils::AddScriptRunner(new MarginSetter(mainWidget)); +} + +nsresult ChromeObserver::HideWindowChrome(bool aShouldHide) { + // only top level chrome documents can hide the window chrome + if (!mDocument->IsRootDisplayDocument()) return NS_OK; + + nsPresContext* presContext = mDocument->GetPresContext(); + + if (presContext && presContext->IsChrome()) { + nsIFrame* frame = mDocument->GetDocumentElement()->GetPrimaryFrame(); + + if (frame) { + nsView* view = frame->GetClosestView(); + + if (view) { + nsIWidget* w = view->GetWidget(); + NS_ENSURE_STATE(w); + w->HideWindowChrome(aShouldHide); + } + } + } + + return NS_OK; +} + +} // namespace dom +} // namespace mozilla |