/* -*- 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 "nsContextMenuInfo.h" #include "nsIImageLoadingContent.h" #include "imgLoader.h" #include "nsIDOMDocument.h" #include "nsIDOMHTMLDocument.h" #include "nsIDOMHTMLElement.h" #include "nsIDOMHTMLHtmlElement.h" #include "nsIDOMHTMLAnchorElement.h" #include "nsIDOMHTMLImageElement.h" #include "nsIDOMHTMLAreaElement.h" #include "nsIDOMHTMLLinkElement.h" #include "nsIDOMWindow.h" #include "nsICSSDeclaration.h" #include "nsIDOMCSSValue.h" #include "nsIDOMCSSPrimitiveValue.h" #include "nsNetUtil.h" #include "nsUnicharUtils.h" #include "nsIDocument.h" #include "nsIPrincipal.h" #include "nsIContentSecurityPolicy.h" #include "nsIContentPolicy.h" #include "imgRequestProxy.h" using mozilla::dom::Element; using mozilla::ErrorResult; NS_IMPL_ISUPPORTS(nsContextMenuInfo, nsIContextMenuInfo) nsContextMenuInfo::nsContextMenuInfo() { } nsContextMenuInfo::~nsContextMenuInfo() { } NS_IMETHODIMP nsContextMenuInfo::GetMouseEvent(nsIDOMEvent** aEvent) { NS_ENSURE_ARG_POINTER(aEvent); NS_IF_ADDREF(*aEvent = mMouseEvent); return NS_OK; } NS_IMETHODIMP nsContextMenuInfo::GetTargetNode(nsIDOMNode** aNode) { NS_ENSURE_ARG_POINTER(aNode); NS_IF_ADDREF(*aNode = mDOMNode); return NS_OK; } NS_IMETHODIMP nsContextMenuInfo::GetAssociatedLink(nsAString& aHRef) { NS_ENSURE_STATE(mAssociatedLink); aHRef.Truncate(0); nsCOMPtr content(do_QueryInterface(mAssociatedLink)); nsAutoString localName; if (content) { content->GetLocalName(localName); } nsCOMPtr linkContent; ToLowerCase(localName); if (localName.EqualsLiteral("a") || localName.EqualsLiteral("area") || localName.EqualsLiteral("link")) { bool hasAttr; content->HasAttribute(NS_LITERAL_STRING("href"), &hasAttr); if (hasAttr) { linkContent = content; nsCOMPtr anchor(do_QueryInterface(linkContent)); if (anchor) { anchor->GetHref(aHRef); } else { nsCOMPtr area(do_QueryInterface(linkContent)); if (area) { area->GetHref(aHRef); } else { nsCOMPtr link(do_QueryInterface(linkContent)); if (link) { link->GetHref(aHRef); } } } } } else { nsCOMPtr curr; mAssociatedLink->GetParentNode(getter_AddRefs(curr)); while (curr) { content = do_QueryInterface(curr); if (!content) { break; } content->GetLocalName(localName); ToLowerCase(localName); if (localName.EqualsLiteral("a")) { bool hasAttr; content->HasAttribute(NS_LITERAL_STRING("href"), &hasAttr); if (hasAttr) { linkContent = content; nsCOMPtr anchor( do_QueryInterface(linkContent)); if (anchor) { anchor->GetHref(aHRef); } } else { linkContent = nullptr; // Links can't be nested. } break; } nsCOMPtr temp = curr; temp->GetParentNode(getter_AddRefs(curr)); } } return NS_OK; } NS_IMETHODIMP nsContextMenuInfo::GetImageContainer(imgIContainer** aImageContainer) { NS_ENSURE_ARG_POINTER(aImageContainer); NS_ENSURE_STATE(mDOMNode); nsCOMPtr request; GetImageRequest(mDOMNode, getter_AddRefs(request)); if (request) { return request->GetImage(aImageContainer); } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsContextMenuInfo::GetImageSrc(nsIURI** aURI) { NS_ENSURE_ARG_POINTER(aURI); NS_ENSURE_STATE(mDOMNode); nsCOMPtr content(do_QueryInterface(mDOMNode)); NS_ENSURE_TRUE(content, NS_ERROR_FAILURE); return content->GetCurrentURI(aURI); } NS_IMETHODIMP nsContextMenuInfo::GetBackgroundImageContainer(imgIContainer** aImageContainer) { NS_ENSURE_ARG_POINTER(aImageContainer); NS_ENSURE_STATE(mDOMNode); RefPtr request; GetBackgroundImageRequest(mDOMNode, getter_AddRefs(request)); if (request) { return request->GetImage(aImageContainer); } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsContextMenuInfo::GetBackgroundImageSrc(nsIURI** aURI) { NS_ENSURE_ARG_POINTER(aURI); NS_ENSURE_STATE(mDOMNode); RefPtr request; GetBackgroundImageRequest(mDOMNode, getter_AddRefs(request)); if (request) { return request->GetURI(aURI); } return NS_ERROR_FAILURE; } nsresult nsContextMenuInfo::GetImageRequest(nsIDOMNode* aDOMNode, imgIRequest** aRequest) { NS_ENSURE_ARG(aDOMNode); NS_ENSURE_ARG_POINTER(aRequest); // Get content nsCOMPtr content(do_QueryInterface(aDOMNode)); NS_ENSURE_TRUE(content, NS_ERROR_FAILURE); return content->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, aRequest); } bool nsContextMenuInfo::HasBackgroundImage(nsIDOMNode* aDOMNode) { NS_ENSURE_TRUE(aDOMNode, false); RefPtr request; GetBackgroundImageRequest(aDOMNode, getter_AddRefs(request)); return (request != nullptr); } nsresult nsContextMenuInfo::GetBackgroundImageRequest(nsIDOMNode* aDOMNode, imgRequestProxy** aRequest) { NS_ENSURE_ARG(aDOMNode); NS_ENSURE_ARG_POINTER(aRequest); nsCOMPtr domNode = aDOMNode; // special case for the element: if it has no background-image // we'll defer to nsCOMPtr htmlElement = do_QueryInterface(domNode); if (htmlElement) { nsCOMPtr element = do_QueryInterface(domNode); nsAutoString nameSpace; element->GetNamespaceURI(nameSpace); if (nameSpace.IsEmpty()) { nsresult rv = GetBackgroundImageRequestInternal(domNode, aRequest); if (NS_SUCCEEDED(rv) && *aRequest) { return NS_OK; } // no background-image found nsCOMPtr document; domNode->GetOwnerDocument(getter_AddRefs(document)); nsCOMPtr htmlDocument(do_QueryInterface(document)); NS_ENSURE_TRUE(htmlDocument, NS_ERROR_FAILURE); nsCOMPtr body; htmlDocument->GetBody(getter_AddRefs(body)); domNode = do_QueryInterface(body); NS_ENSURE_TRUE(domNode, NS_ERROR_FAILURE); } } return GetBackgroundImageRequestInternal(domNode, aRequest); } nsresult nsContextMenuInfo::GetBackgroundImageRequestInternal(nsIDOMNode* aDOMNode, imgRequestProxy** aRequest) { NS_ENSURE_ARG_POINTER(aDOMNode); nsCOMPtr domNode = aDOMNode; nsCOMPtr parentNode; nsCOMPtr document; domNode->GetOwnerDocument(getter_AddRefs(document)); NS_ENSURE_TRUE(document, NS_ERROR_FAILURE); nsCOMPtr window; document->GetDefaultView(getter_AddRefs(window)); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); auto* piWindow = nsPIDOMWindowOuter::From(window); nsPIDOMWindowInner* innerWindow = piWindow->GetCurrentInnerWindow(); MOZ_ASSERT(innerWindow); nsCOMPtr primitiveValue; nsAutoString bgStringValue; nsCOMPtr doc(do_QueryInterface(document)); nsCOMPtr principal = doc ? doc->NodePrincipal() : nullptr; while (true) { nsCOMPtr domElement(do_QueryInterface(domNode)); // bail for the parent node of the root element or null argument if (!domElement) { break; } ErrorResult dummy; nsCOMPtr computedStyle = innerWindow->GetComputedStyle(*domElement, EmptyString(), dummy); dummy.SuppressException(); if (computedStyle) { nsCOMPtr cssValue; computedStyle->GetPropertyCSSValue(NS_LITERAL_STRING("background-image"), getter_AddRefs(cssValue)); primitiveValue = do_QueryInterface(cssValue); if (primitiveValue) { primitiveValue->GetStringValue(bgStringValue); if (!bgStringValue.EqualsLiteral("none")) { nsCOMPtr bgUri; NS_NewURI(getter_AddRefs(bgUri), bgStringValue); NS_ENSURE_TRUE(bgUri, NS_ERROR_FAILURE); imgLoader* il = imgLoader::NormalLoader(); NS_ENSURE_TRUE(il, NS_ERROR_FAILURE); return il->LoadImage(bgUri, nullptr, nullptr, doc->GetReferrerPolicy(), principal, nullptr, nullptr, nullptr, nullptr, nsIRequest::LOAD_NORMAL, nullptr, nsIContentPolicy::TYPE_INTERNAL_IMAGE, EmptyString(), /* aUseUrgentStartForChannel */ false, aRequest); } } // bail if we encounter non-transparent background-color computedStyle->GetPropertyCSSValue(NS_LITERAL_STRING("background-color"), getter_AddRefs(cssValue)); primitiveValue = do_QueryInterface(cssValue); if (primitiveValue) { primitiveValue->GetStringValue(bgStringValue); if (!bgStringValue.EqualsLiteral("transparent")) { return NS_ERROR_FAILURE; } } } domNode->GetParentNode(getter_AddRefs(parentNode)); domNode = parentNode; } return NS_ERROR_FAILURE; }