gecko-dev/docshell/base/nsContextMenuInfo.cpp
Tom Tung 0e308b0c4f Bug 1348050 - Part 3: Mark channel as urgent-start for loading image. r=baku,mayhemer
This part is mainly to mark the channel as urgent-start if src related
attributes in HTMLImageElement and HTMLInputElement is set and the channel is
open due to user interaction. Unfortunately, we cannot just check the event
state just after creating channel since some loading image tasks will be queue
and execute in stable state. Thus, I store the event state in elements and
pass it to the place where create the channel.

MozReview-Commit-ID: GBdAkPfVzsn

--HG--
extra : rebase_source : 715352317b4b600f8a7f78b7bc22b894bb272d27
2017-04-25 09:17:38 +08:00

328 lines
9.4 KiB
C++

/* -*- 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<nsIDOMElement> content(do_QueryInterface(mAssociatedLink));
nsAutoString localName;
if (content) {
content->GetLocalName(localName);
}
nsCOMPtr<nsIDOMElement> 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<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(linkContent));
if (anchor) {
anchor->GetHref(aHRef);
} else {
nsCOMPtr<nsIDOMHTMLAreaElement> area(do_QueryInterface(linkContent));
if (area) {
area->GetHref(aHRef);
} else {
nsCOMPtr<nsIDOMHTMLLinkElement> link(do_QueryInterface(linkContent));
if (link) {
link->GetHref(aHRef);
}
}
}
}
} else {
nsCOMPtr<nsIDOMNode> 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<nsIDOMHTMLAnchorElement> anchor(
do_QueryInterface(linkContent));
if (anchor) {
anchor->GetHref(aHRef);
}
} else {
linkContent = nullptr; // Links can't be nested.
}
break;
}
nsCOMPtr<nsIDOMNode> 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<imgIRequest> 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<nsIImageLoadingContent> 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<imgRequestProxy> 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<imgRequestProxy> 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<nsIImageLoadingContent> 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<imgRequestProxy> 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<nsIDOMNode> domNode = aDOMNode;
// special case for the <html> element: if it has no background-image
// we'll defer to <body>
nsCOMPtr<nsIDOMHTMLHtmlElement> htmlElement = do_QueryInterface(domNode);
if (htmlElement) {
nsCOMPtr<nsIDOMHTMLElement> 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<nsIDOMDocument> document;
domNode->GetOwnerDocument(getter_AddRefs(document));
nsCOMPtr<nsIDOMHTMLDocument> htmlDocument(do_QueryInterface(document));
NS_ENSURE_TRUE(htmlDocument, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMHTMLElement> 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<nsIDOMNode> domNode = aDOMNode;
nsCOMPtr<nsIDOMNode> parentNode;
nsCOMPtr<nsIDOMDocument> document;
domNode->GetOwnerDocument(getter_AddRefs(document));
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
nsCOMPtr<mozIDOMWindowProxy> 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<nsIDOMCSSPrimitiveValue> primitiveValue;
nsAutoString bgStringValue;
nsCOMPtr<nsIDocument> doc(do_QueryInterface(document));
nsCOMPtr<nsIPrincipal> principal = doc ? doc->NodePrincipal() : nullptr;
while (true) {
nsCOMPtr<Element> domElement(do_QueryInterface(domNode));
// bail for the parent node of the root element or null argument
if (!domElement) {
break;
}
ErrorResult dummy;
nsCOMPtr<nsICSSDeclaration> computedStyle =
innerWindow->GetComputedStyle(*domElement, EmptyString(), dummy);
dummy.SuppressException();
if (computedStyle) {
nsCOMPtr<nsIDOMCSSValue> 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<nsIURI> 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;
}