mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Merge inbound to mozilla-central r=merge a=merge
This commit is contained in:
commit
282fd1a852
@ -41,8 +41,6 @@ XPIDL_SOURCES += [
|
||||
'nsIContentViewer.idl',
|
||||
'nsIContentViewerContainer.idl',
|
||||
'nsIContentViewerEdit.idl',
|
||||
'nsIContextMenuListener.idl',
|
||||
'nsIContextMenuListener2.idl',
|
||||
'nsIDocCharset.idl',
|
||||
'nsIDocShell.idl',
|
||||
'nsIDocShellLoadInfo.idl',
|
||||
@ -89,7 +87,6 @@ EXPORTS.mozilla.dom += [
|
||||
UNIFIED_SOURCES += [
|
||||
'LoadContext.cpp',
|
||||
'nsAboutRedirector.cpp',
|
||||
'nsContextMenuInfo.cpp',
|
||||
'nsDefaultURIFixup.cpp',
|
||||
'nsDocShell.cpp',
|
||||
'nsDocShellEditorData.cpp',
|
||||
|
@ -1,320 +0,0 @@
|
||||
/* -*- 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 "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"
|
||||
#include "mozilla/dom/HTMLAnchorElement.h"
|
||||
#include "mozilla/dom/HTMLAreaElement.h"
|
||||
#include "mozilla/dom/HTMLLinkElement.h"
|
||||
|
||||
using mozilla::dom::HTMLAnchorElement;
|
||||
using mozilla::dom::HTMLAreaElement;
|
||||
using mozilla::dom::HTMLLinkElement;
|
||||
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<nsIContent> content(do_QueryInterface(mAssociatedLink));
|
||||
nsCOMPtr<nsIContent> linkContent;
|
||||
if (content &&
|
||||
content->IsAnyOfHTMLElements(nsGkAtoms::a,
|
||||
nsGkAtoms::area,
|
||||
nsGkAtoms::link)) {
|
||||
bool hasAttr = content->HasAttr(kNameSpaceID_None, nsGkAtoms::href);
|
||||
if (hasAttr) {
|
||||
linkContent = content;
|
||||
RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromContent(linkContent);
|
||||
if (anchor) {
|
||||
anchor->GetHref(aHRef);
|
||||
} else {
|
||||
RefPtr<HTMLAreaElement> area = HTMLAreaElement::FromContent(linkContent);
|
||||
if (area) {
|
||||
area->GetHref(aHRef);
|
||||
} else {
|
||||
RefPtr<HTMLLinkElement> link = HTMLLinkElement::FromContent(linkContent);
|
||||
if (link) {
|
||||
link->GetHref(aHRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nsCOMPtr<nsIDOMNode> curr;
|
||||
mAssociatedLink->GetParentNode(getter_AddRefs(curr));
|
||||
while (curr) {
|
||||
content = do_QueryInterface(curr);
|
||||
if (!content) {
|
||||
break;
|
||||
}
|
||||
if (content->IsHTMLElement(nsGkAtoms::a)) {
|
||||
bool hasAttr;
|
||||
hasAttr = content->HasAttr(kNameSpaceID_None, nsGkAtoms::href);
|
||||
if (hasAttr) {
|
||||
linkContent = content;
|
||||
RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromContent(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, 0, 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;
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/* -*- 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 nsContextMenuInfo_h__
|
||||
#define nsContextMenuInfo_h__
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIContextMenuListener2.h"
|
||||
#include "nsIDOMNode.h"
|
||||
#include "nsIDOMEvent.h"
|
||||
#include "imgIContainer.h"
|
||||
#include "imgIRequest.h"
|
||||
|
||||
class ChromeContextMenuListener;
|
||||
class imgRequestProxy;
|
||||
|
||||
// Helper class for implementors of nsIContextMenuListener2
|
||||
class nsContextMenuInfo : public nsIContextMenuInfo
|
||||
{
|
||||
friend class ChromeContextMenuListener;
|
||||
|
||||
public:
|
||||
nsContextMenuInfo();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICONTEXTMENUINFO
|
||||
|
||||
private:
|
||||
virtual ~nsContextMenuInfo();
|
||||
|
||||
void SetMouseEvent(nsIDOMEvent* aEvent) { mMouseEvent = aEvent; }
|
||||
void SetDOMNode(nsIDOMNode* aNode) { mDOMNode = aNode; }
|
||||
void SetAssociatedLink(nsIDOMNode* aLink) { mAssociatedLink = aLink; }
|
||||
|
||||
nsresult GetImageRequest(nsIDOMNode* aDOMNode, imgIRequest** aRequest);
|
||||
|
||||
bool HasBackgroundImage(nsIDOMNode* aDOMNode);
|
||||
|
||||
nsresult GetBackgroundImageRequest(nsIDOMNode* aDOMNode,
|
||||
imgRequestProxy** aRequest);
|
||||
|
||||
nsresult GetBackgroundImageRequestInternal(nsIDOMNode* aDOMNode,
|
||||
imgRequestProxy** aRequest);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIDOMEvent> mMouseEvent;
|
||||
nsCOMPtr<nsIDOMNode> mDOMNode;
|
||||
nsCOMPtr<nsIDOMNode> mAssociatedLink;
|
||||
};
|
||||
|
||||
#endif // nsContextMenuInfo_h__
|
@ -15,6 +15,7 @@
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/HTMLAnchorElement.h"
|
||||
#include "mozilla/dom/PendingGlobalHistoryEntry.h"
|
||||
#include "mozilla/dom/TabChild.h"
|
||||
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
|
||||
|
@ -24,8 +24,6 @@
|
||||
|
||||
// Interfaces needed to be included
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIContextMenuListener.h"
|
||||
#include "nsIContextMenuListener2.h"
|
||||
#include "nsITooltipListener.h"
|
||||
#include "nsIDOMNode.h"
|
||||
#include "nsIDOMNodeList.h"
|
||||
@ -59,7 +57,6 @@
|
||||
#include "nsIWebBrowserChromeFocus.h"
|
||||
#include "nsIContent.h"
|
||||
#include "imgIContainer.h"
|
||||
#include "nsContextMenuInfo.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsViewManager.h"
|
||||
#include "nsView.h"
|
||||
@ -898,19 +895,6 @@ nsDocShellTreeOwner::AddChromeListeners()
|
||||
}
|
||||
}
|
||||
|
||||
// install context menus
|
||||
if (!mChromeContextMenuListener) {
|
||||
nsCOMPtr<nsIContextMenuListener2> contextListener2(
|
||||
do_QueryInterface(webBrowserChrome));
|
||||
nsCOMPtr<nsIContextMenuListener> contextListener(
|
||||
do_QueryInterface(webBrowserChrome));
|
||||
if (contextListener2 || contextListener) {
|
||||
mChromeContextMenuListener =
|
||||
new ChromeContextMenuListener(mWebBrowser, webBrowserChrome);
|
||||
rv = mChromeContextMenuListener->AddChromeListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// register dragover and drop event listeners with the listener manager
|
||||
nsCOMPtr<EventTarget> target;
|
||||
GetDOMEventTarget(mWebBrowser, getter_AddRefs(target));
|
||||
@ -933,10 +917,6 @@ nsDocShellTreeOwner::RemoveChromeListeners()
|
||||
mChromeTooltipListener->RemoveChromeListeners();
|
||||
mChromeTooltipListener = nullptr;
|
||||
}
|
||||
if (mChromeContextMenuListener) {
|
||||
mChromeContextMenuListener->RemoveChromeListeners();
|
||||
mChromeContextMenuListener = nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<EventTarget> piTarget;
|
||||
GetDOMEventTarget(mWebBrowser, getter_AddRefs(piTarget));
|
||||
@ -1406,276 +1386,3 @@ ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer,
|
||||
self->mPossibleTooltipNode = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(ChromeContextMenuListener, nsIDOMEventListener)
|
||||
|
||||
ChromeContextMenuListener::ChromeContextMenuListener(
|
||||
nsWebBrowser* aInBrowser,
|
||||
nsIWebBrowserChrome* aInChrome)
|
||||
: mContextMenuListenerInstalled(false)
|
||||
, mWebBrowser(aInBrowser)
|
||||
, mWebBrowserChrome(aInChrome)
|
||||
{
|
||||
}
|
||||
|
||||
ChromeContextMenuListener::~ChromeContextMenuListener()
|
||||
{
|
||||
}
|
||||
|
||||
// Subscribe to the events that will allow us to track context menus. Bascially,
|
||||
// this is just the context-menu DOM event.
|
||||
NS_IMETHODIMP
|
||||
ChromeContextMenuListener::AddContextMenuListener()
|
||||
{
|
||||
if (mEventTarget) {
|
||||
nsresult rv = mEventTarget->AddEventListener(
|
||||
NS_LITERAL_STRING("contextmenu"), this, false, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mContextMenuListenerInstalled = true;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Unsubscribe from all the various context menu events that we were listening
|
||||
// to.
|
||||
NS_IMETHODIMP
|
||||
ChromeContextMenuListener::RemoveContextMenuListener()
|
||||
{
|
||||
if (mEventTarget) {
|
||||
nsresult rv = mEventTarget->RemoveEventListener(
|
||||
NS_LITERAL_STRING("contextmenu"), this, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mContextMenuListenerInstalled = false;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Hook up things to the chrome like context menus and tooltips, if the chrome
|
||||
// has implemented the right interfaces.
|
||||
NS_IMETHODIMP
|
||||
ChromeContextMenuListener::AddChromeListeners()
|
||||
{
|
||||
if (!mEventTarget) {
|
||||
GetDOMEventTarget(mWebBrowser, getter_AddRefs(mEventTarget));
|
||||
}
|
||||
|
||||
// Register the appropriate events for context menus, but only if
|
||||
// the embedding chrome cares.
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
nsCOMPtr<nsIContextMenuListener2> contextListener2(
|
||||
do_QueryInterface(mWebBrowserChrome));
|
||||
nsCOMPtr<nsIContextMenuListener> contextListener(
|
||||
do_QueryInterface(mWebBrowserChrome));
|
||||
if ((contextListener || contextListener2) && !mContextMenuListenerInstalled) {
|
||||
rv = AddContextMenuListener();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Unsubscribe from the various things we've hooked up to the window root.
|
||||
NS_IMETHODIMP
|
||||
ChromeContextMenuListener::RemoveChromeListeners()
|
||||
{
|
||||
if (mContextMenuListenerInstalled) {
|
||||
RemoveContextMenuListener();
|
||||
}
|
||||
|
||||
mEventTarget = nullptr;
|
||||
|
||||
// it really doesn't matter if these fail...
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We're on call to show the context menu. Dig around in the DOM to find the
|
||||
// type of object we're dealing with and notify the front end chrome.
|
||||
NS_IMETHODIMP
|
||||
ChromeContextMenuListener::HandleEvent(nsIDOMEvent* aMouseEvent)
|
||||
{
|
||||
nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
|
||||
NS_ENSURE_TRUE(mouseEvent, NS_ERROR_UNEXPECTED);
|
||||
|
||||
bool isDefaultPrevented = false;
|
||||
aMouseEvent->GetDefaultPrevented(&isDefaultPrevented);
|
||||
if (isDefaultPrevented) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<EventTarget> targetNode =
|
||||
aMouseEvent->InternalDOMEvent()->GetTarget();
|
||||
if (!targetNode) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMNode> targetDOMnode;
|
||||
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(targetNode);
|
||||
if (!node) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Stop the context menu event going to other windows (bug 78396)
|
||||
aMouseEvent->PreventDefault();
|
||||
|
||||
// If the listener is a nsIContextMenuListener2, create the info object
|
||||
nsCOMPtr<nsIContextMenuListener2> menuListener2(
|
||||
do_QueryInterface(mWebBrowserChrome));
|
||||
nsContextMenuInfo* menuInfoImpl = nullptr;
|
||||
nsCOMPtr<nsIContextMenuInfo> menuInfo;
|
||||
if (menuListener2) {
|
||||
menuInfoImpl = new nsContextMenuInfo;
|
||||
menuInfo = menuInfoImpl;
|
||||
}
|
||||
|
||||
uint32_t flags = nsIContextMenuListener::CONTEXT_NONE;
|
||||
uint32_t flags2 = nsIContextMenuListener2::CONTEXT_NONE;
|
||||
|
||||
// XXX test for selected text
|
||||
|
||||
uint16_t nodeType;
|
||||
nsresult res = node->GetNodeType(&nodeType);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
// First, checks for nodes that never have children.
|
||||
if (nodeType == nsIDOMNode::ELEMENT_NODE) {
|
||||
nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(node));
|
||||
if (imageContent) {
|
||||
nsCOMPtr<nsIURI> imgUri;
|
||||
imageContent->GetCurrentURI(getter_AddRefs(imgUri));
|
||||
if (imgUri) {
|
||||
flags |= nsIContextMenuListener::CONTEXT_IMAGE;
|
||||
flags2 |= nsIContextMenuListener2::CONTEXT_IMAGE;
|
||||
targetDOMnode = node;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(node));
|
||||
if (formControl) {
|
||||
if (formControl->ControlType() == NS_FORM_TEXTAREA) {
|
||||
flags |= nsIContextMenuListener::CONTEXT_TEXT;
|
||||
flags2 |= nsIContextMenuListener2::CONTEXT_TEXT;
|
||||
targetDOMnode = node;
|
||||
} else {
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> inputElement(
|
||||
do_QueryInterface(formControl));
|
||||
if (inputElement) {
|
||||
flags |= nsIContextMenuListener::CONTEXT_INPUT;
|
||||
flags2 |= nsIContextMenuListener2::CONTEXT_INPUT;
|
||||
|
||||
if (menuListener2) {
|
||||
if (formControl->IsSingleLineTextControl(false)) {
|
||||
flags2 |= nsIContextMenuListener2::CONTEXT_TEXT;
|
||||
}
|
||||
}
|
||||
|
||||
targetDOMnode = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// always consume events for plugins who may throw their own context menus
|
||||
// but not for image objects. Document objects will never be targets or
|
||||
// ancestors of targets, so that's OK.
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(node);
|
||||
if (content &&
|
||||
(content->IsHTMLElement(nsGkAtoms::embed) ||
|
||||
(!(flags & nsIContextMenuListener::CONTEXT_IMAGE) &&
|
||||
content->IsHTMLElement(nsGkAtoms::object)))) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Bubble out, looking for items of interest
|
||||
do {
|
||||
uint16_t nodeType;
|
||||
res = node->GetNodeType(&nodeType);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
if (nodeType == nsIDOMNode::ELEMENT_NODE) {
|
||||
|
||||
// Test if the element has an associated link
|
||||
nsCOMPtr<nsIDOMElement> element(do_QueryInterface(node));
|
||||
|
||||
bool hasAttr = false;
|
||||
res = element->HasAttribute(NS_LITERAL_STRING("href"), &hasAttr);
|
||||
|
||||
if (NS_SUCCEEDED(res) && hasAttr) {
|
||||
flags |= nsIContextMenuListener::CONTEXT_LINK;
|
||||
flags2 |= nsIContextMenuListener2::CONTEXT_LINK;
|
||||
if (!targetDOMnode) {
|
||||
targetDOMnode = node;
|
||||
}
|
||||
if (menuInfoImpl) {
|
||||
menuInfoImpl->SetAssociatedLink(node);
|
||||
}
|
||||
break; // exit do-while
|
||||
}
|
||||
}
|
||||
|
||||
// walk-up-the-tree
|
||||
nsCOMPtr<nsIDOMNode> parentNode;
|
||||
node->GetParentNode(getter_AddRefs(parentNode));
|
||||
node = parentNode;
|
||||
} while (node);
|
||||
|
||||
if (!flags && !flags2) {
|
||||
// We found nothing of interest so far, check if we
|
||||
// have at least an html document.
|
||||
nsCOMPtr<nsIDOMDocument> document;
|
||||
node = do_QueryInterface(targetNode);
|
||||
node->GetOwnerDocument(getter_AddRefs(document));
|
||||
nsCOMPtr<nsIDOMHTMLDocument> htmlDocument(do_QueryInterface(document));
|
||||
if (htmlDocument) {
|
||||
flags |= nsIContextMenuListener::CONTEXT_DOCUMENT;
|
||||
flags2 |= nsIContextMenuListener2::CONTEXT_DOCUMENT;
|
||||
targetDOMnode = node;
|
||||
if (!(flags & nsIContextMenuListener::CONTEXT_IMAGE)) {
|
||||
// check if this is a background image that the user was trying to click
|
||||
// on and if the listener is ready for that (only
|
||||
// nsIContextMenuListener2 and up)
|
||||
if (menuInfoImpl && menuInfoImpl->HasBackgroundImage(targetDOMnode)) {
|
||||
flags2 |= nsIContextMenuListener2::CONTEXT_BACKGROUND_IMAGE;
|
||||
// For the embedder to get the correct background image
|
||||
// targetDOMnode must point to the original node.
|
||||
targetDOMnode = do_QueryInterface(targetNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we need to cache the event target into the focus controller's popupNode
|
||||
// so we can get at it later from command code, etc.:
|
||||
|
||||
// get the dom window
|
||||
nsCOMPtr<mozIDOMWindowProxy> win;
|
||||
res = mWebBrowser->GetContentDOMWindow(getter_AddRefs(win));
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
|
||||
|
||||
auto* window = nsPIDOMWindowOuter::From(win);
|
||||
nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
|
||||
NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
|
||||
if (root) {
|
||||
// set the window root's popup node to the event target
|
||||
root->SetPopupNode(targetDOMnode);
|
||||
}
|
||||
|
||||
// Tell the listener all about the event
|
||||
if (menuListener2) {
|
||||
menuInfoImpl->SetMouseEvent(aMouseEvent);
|
||||
menuInfoImpl->SetDOMNode(targetDOMnode);
|
||||
menuListener2->OnShowContextMenu(flags2, menuInfo);
|
||||
} else {
|
||||
nsCOMPtr<nsIContextMenuListener> menuListener(
|
||||
do_QueryInterface(mWebBrowserChrome));
|
||||
if (menuListener) {
|
||||
menuListener->OnShowContextMenu(flags, aMouseEvent, targetDOMnode);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ class EventTarget;
|
||||
|
||||
class nsWebBrowser;
|
||||
class ChromeTooltipListener;
|
||||
class ChromeContextMenuListener;
|
||||
|
||||
// {6D10C180-6888-11d4-952B-0020183BF181}
|
||||
#define NS_ICDOCSHELLTREEOWNER_IID \
|
||||
@ -120,7 +119,6 @@ protected:
|
||||
// They are separate objects to avoid circular references between |this|
|
||||
// and the DOM.
|
||||
RefPtr<ChromeTooltipListener> mChromeTooltipListener;
|
||||
RefPtr<ChromeContextMenuListener> mChromeContextMenuListener;
|
||||
|
||||
RefPtr<nsDocShellTreeOwner> mContentTreeOwner;
|
||||
|
||||
@ -203,37 +201,4 @@ private:
|
||||
nsCOMPtr<nsIDOMNode> mPossibleTooltipNode;
|
||||
};
|
||||
|
||||
// The class that listens to the chrome events and tells the embedding chrome to
|
||||
// show context menus, as appropriate. Handles registering itself with the DOM
|
||||
// with AddChromeListeners() and removing itself with RemoveChromeListeners().
|
||||
class ChromeContextMenuListener : public nsIDOMEventListener
|
||||
{
|
||||
protected:
|
||||
virtual ~ChromeContextMenuListener();
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
ChromeContextMenuListener(nsWebBrowser* aInBrowser,
|
||||
nsIWebBrowserChrome* aInChrome);
|
||||
|
||||
// nsIDOMContextMenuListener
|
||||
NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) override;
|
||||
|
||||
// Add/remove the relevant listeners, based on what interfaces
|
||||
// the embedding chrome implements.
|
||||
NS_IMETHOD AddChromeListeners();
|
||||
NS_IMETHOD RemoveChromeListeners();
|
||||
|
||||
private:
|
||||
NS_IMETHOD AddContextMenuListener();
|
||||
NS_IMETHOD RemoveContextMenuListener();
|
||||
|
||||
bool mContextMenuListenerInstalled;
|
||||
|
||||
nsWebBrowser* mWebBrowser;
|
||||
nsCOMPtr<mozilla::dom::EventTarget> mEventTarget;
|
||||
nsCOMPtr<nsIWebBrowserChrome> mWebBrowserChrome;
|
||||
};
|
||||
|
||||
#endif /* nsDocShellTreeOwner_h__ */
|
||||
|
@ -1,65 +0,0 @@
|
||||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 nsIDOMEvent;
|
||||
interface nsIDOMNode;
|
||||
|
||||
/**
|
||||
* An optional interface for embedding clients wishing to receive
|
||||
* notifications for context menu events (e.g. generated by
|
||||
* a user right-mouse clicking on a link). The embedder implements
|
||||
* this interface on the web browser chrome object associated
|
||||
* with the window that notifications are required for. When a context
|
||||
* menu event, the browser will call this interface if present.
|
||||
*
|
||||
* @see nsIDOMNode
|
||||
* @see nsIDOMEvent
|
||||
*/
|
||||
[scriptable, uuid(3478b6b0-3875-11d4-94ef-0020183bf181)]
|
||||
interface nsIContextMenuListener : nsISupports
|
||||
{
|
||||
/** Flag. No context. */
|
||||
const unsigned long CONTEXT_NONE = 0;
|
||||
/** Flag. Context is a link element. */
|
||||
const unsigned long CONTEXT_LINK = 1;
|
||||
/** Flag. Context is an image element. */
|
||||
const unsigned long CONTEXT_IMAGE = 2;
|
||||
/** Flag. Context is the whole document. */
|
||||
const unsigned long CONTEXT_DOCUMENT = 4;
|
||||
/** Flag. Context is a text area element. */
|
||||
const unsigned long CONTEXT_TEXT = 8;
|
||||
/** Flag. Context is an input element. */
|
||||
const unsigned long CONTEXT_INPUT = 16;
|
||||
|
||||
/**
|
||||
* Called when the browser receives a context menu event (e.g. user is right-mouse
|
||||
* clicking somewhere on the document). The combination of flags, event and node
|
||||
* provided in the call indicate where and what was clicked on.
|
||||
*
|
||||
* The following table describes what context flags and node combinations are
|
||||
* possible.
|
||||
*
|
||||
* <TABLE>
|
||||
* <TR><TD><B>aContextFlag</B></TD><TD>aNode</TD></TR>
|
||||
* <TR><TD>CONTEXT_LINK</TD><TD><A></TD></TR>
|
||||
* <TR><TD>CONTEXT_IMAGE</TD><TD><IMG></TD></TR>
|
||||
* <TR><TD>CONTEXT_IMAGE | CONTEXT_LINK</TD><TD><IMG>
|
||||
* with an <A> as an ancestor</TD></TR>
|
||||
* <TR><TD>CONTEXT_INPUT</TD><TD><INPUT></TD></TR>
|
||||
* <TR><TD>CONTEXT_TEXT</TD><TD><TEXTAREA></TD></TR>
|
||||
* <TR><TD>CONTEXT_DOCUMENT</TD><TD><HTML></TD></TR>
|
||||
* </TABLE>
|
||||
*
|
||||
* @param aContextFlags Flags indicating the kind of context.
|
||||
* @param aEvent The DOM context menu event.
|
||||
* @param aNode The DOM node most relevant to the context.
|
||||
*
|
||||
* @return <CODE>NS_OK</CODE> always.
|
||||
*/
|
||||
void onShowContextMenu(in unsigned long aContextFlags, in nsIDOMEvent aEvent, in nsIDOMNode aNode);
|
||||
};
|
||||
|
@ -1,121 +0,0 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 nsIDOMEvent;
|
||||
interface nsIDOMNode;
|
||||
interface imgIContainer;
|
||||
interface nsIURI;
|
||||
interface nsIContextMenuInfo;
|
||||
|
||||
/* THIS IS A PUBLIC EMBEDDING API */
|
||||
|
||||
/**
|
||||
* nsIContextMenuListener2
|
||||
*
|
||||
* This is an extended version of nsIContextMenuListener
|
||||
* It provides a helper class, nsIContextMenuInfo, to allow access to
|
||||
* background images as well as various utilities.
|
||||
*
|
||||
* @see nsIContextMenuListener
|
||||
* @see nsIContextMenuInfo
|
||||
*/
|
||||
|
||||
[scriptable, uuid(7fb719b3-d804-4964-9596-77cf924ee314)]
|
||||
interface nsIContextMenuListener2 : nsISupports
|
||||
{
|
||||
/** Flag. No context. */
|
||||
const unsigned long CONTEXT_NONE = 0;
|
||||
/** Flag. Context is a link element. */
|
||||
const unsigned long CONTEXT_LINK = 1;
|
||||
/** Flag. Context is an image element. */
|
||||
const unsigned long CONTEXT_IMAGE = 2;
|
||||
/** Flag. Context is the whole document. */
|
||||
const unsigned long CONTEXT_DOCUMENT = 4;
|
||||
/** Flag. Context is a text area element. */
|
||||
const unsigned long CONTEXT_TEXT = 8;
|
||||
/** Flag. Context is an input element. */
|
||||
const unsigned long CONTEXT_INPUT = 16;
|
||||
/** Flag. Context is a background image. */
|
||||
const unsigned long CONTEXT_BACKGROUND_IMAGE = 32;
|
||||
|
||||
/**
|
||||
* Called when the browser receives a context menu event (e.g. user is right-mouse
|
||||
* clicking somewhere on the document). The combination of flags, along with the
|
||||
* attributes of <CODE>aUtils</CODE>, indicate where and what was clicked on.
|
||||
*
|
||||
* The following table describes what context flags and node combinations are
|
||||
* possible.
|
||||
*
|
||||
* aContextFlags aUtils.targetNode
|
||||
*
|
||||
* CONTEXT_LINK <A>
|
||||
* CONTEXT_IMAGE <IMG>
|
||||
* CONTEXT_IMAGE | CONTEXT_LINK <IMG> with <A> as an ancestor
|
||||
* CONTEXT_INPUT <INPUT>
|
||||
* CONTEXT_INPUT | CONTEXT_IMAGE <INPUT> with type=image
|
||||
* CONTEXT_TEXT <TEXTAREA>
|
||||
* CONTEXT_DOCUMENT <HTML>
|
||||
* CONTEXT_BACKGROUND_IMAGE <HTML> with background image
|
||||
*
|
||||
* @param aContextFlags Flags indicating the kind of context.
|
||||
* @param aUtils Context information and helper utilities.
|
||||
*
|
||||
* @see nsIContextMenuInfo
|
||||
*/
|
||||
void onShowContextMenu(in unsigned long aContextFlags, in nsIContextMenuInfo aUtils);
|
||||
};
|
||||
|
||||
/**
|
||||
* nsIContextMenuInfo
|
||||
*
|
||||
* A helper object for implementors of nsIContextMenuListener2.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(2f977d56-5485-11d4-87e2-0010a4e75ef2)]
|
||||
interface nsIContextMenuInfo : nsISupports
|
||||
{
|
||||
/**
|
||||
* The DOM context menu event.
|
||||
*/
|
||||
readonly attribute nsIDOMEvent mouseEvent;
|
||||
|
||||
/**
|
||||
* The DOM node most relevant to the context.
|
||||
*/
|
||||
readonly attribute nsIDOMNode targetNode;
|
||||
|
||||
/**
|
||||
* Given the <CODE>CONTEXT_LINK</CODE> flag, <CODE>targetNode</CODE> may not
|
||||
* nescesarily be a link. This returns the anchor from <CODE>targetNode</CODE>
|
||||
* if it has one or that of its nearest ancestor if it does not.
|
||||
*/
|
||||
readonly attribute AString associatedLink;
|
||||
|
||||
/**
|
||||
* Given the <CODE>CONTEXT_IMAGE</CODE> flag, these methods can be
|
||||
* used in order to get the image for viewing, saving, or for the clipboard.
|
||||
*
|
||||
* @return <CODE>NS_OK</CODE> if successful, otherwise <CODE>NS_ERROR_FAILURE</CODE> if no
|
||||
* image was found, or NS_ERROR_NULL_POINTER if an internal error occurs where we think there
|
||||
* is an image, but for some reason it cannot be returned.
|
||||
*/
|
||||
|
||||
readonly attribute imgIContainer imageContainer;
|
||||
readonly attribute nsIURI imageSrc;
|
||||
|
||||
/**
|
||||
* Given the <CODE>CONTEXT_BACKGROUND_IMAGE</CODE> flag, these methods can be
|
||||
* used in order to get the image for viewing, saving, or for the clipboard.
|
||||
*
|
||||
* @return <CODE>NS_OK</CODE> if successful, otherwise <CODE>NS_ERROR_FAILURE</CODE> if no background
|
||||
* image was found, or NS_ERROR_NULL_POINTER if an internal error occurs where we think there is a
|
||||
* background image, but for some reason it cannot be returned.
|
||||
*/
|
||||
|
||||
readonly attribute imgIContainer backgroundImageContainer;
|
||||
readonly attribute nsIURI backgroundImageSrc;
|
||||
};
|
@ -453,12 +453,11 @@ SurfaceTextureHost::~SurfaceTextureHost()
|
||||
void
|
||||
SurfaceTextureHost::PrepareTextureSource(CompositableTextureSourceRef& aTexture)
|
||||
{
|
||||
GLContext* gl = this->gl();
|
||||
if (!gl || !gl->MakeCurrent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mContinuousUpdate && mSurfTex) {
|
||||
if (!EnsureAttached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// UpdateTexImage() advances the internal buffer queue, so we only want to call this
|
||||
// once per transactionwhen we are not in continuous mode (as we are here). Otherwise,
|
||||
// the SurfaceTexture content will be de-synced from the rest of the page in subsequent
|
||||
@ -474,14 +473,32 @@ SurfaceTextureHost::gl() const
|
||||
}
|
||||
|
||||
bool
|
||||
SurfaceTextureHost::Lock()
|
||||
SurfaceTextureHost::EnsureAttached()
|
||||
{
|
||||
GLContext* gl = this->gl();
|
||||
if (!gl || !gl->MakeCurrent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mSurfTex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GLContext* gl = this->gl();
|
||||
if (!gl || !gl->MakeCurrent()) {
|
||||
if (!mSurfTex->IsAttachedToGLContext((int64_t)gl)) {
|
||||
GLuint texName;
|
||||
gl->fGenTextures(1, &texName);
|
||||
if (NS_FAILED(mSurfTex->AttachToGLContext((int64_t)gl, texName))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SurfaceTextureHost::Lock()
|
||||
{
|
||||
if (!EnsureAttached()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -501,14 +518,6 @@ SurfaceTextureHost::Lock()
|
||||
mIgnoreTransform);
|
||||
}
|
||||
|
||||
if (!mSurfTex->IsAttachedToGLContext((int64_t)gl)) {
|
||||
GLuint texName;
|
||||
gl->fGenTextures(1, &texName);
|
||||
if (NS_FAILED(mSurfTex->AttachToGLContext((int64_t)gl, texName))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -429,6 +429,8 @@ public:
|
||||
virtual const char* Name() override { return "SurfaceTextureHost"; }
|
||||
|
||||
protected:
|
||||
bool EnsureAttached();
|
||||
|
||||
mozilla::java::GeckoSurfaceTexture::GlobalRef mSurfTex;
|
||||
const gfx::IntSize mSize;
|
||||
const gfx::SurfaceFormat mFormat;
|
||||
|
@ -459,24 +459,27 @@ WebRenderCommandBuilder::GenerateFallbackData(nsDisplayItem* aItem,
|
||||
|
||||
bool snap;
|
||||
nsRect itemBounds = aItem->GetBounds(aDisplayListBuilder, &snap);
|
||||
nsRect clippedBounds = itemBounds;
|
||||
|
||||
const DisplayItemClip& clip = aItem->GetClip();
|
||||
// Blob images will only draw the visible area of the blob so we don't need to clip
|
||||
// them here and can just rely on the webrender clipping.
|
||||
if (clip.HasClip() && !gfxPrefs::WebRenderBlobImages()) {
|
||||
clippedBounds = itemBounds.Intersect(clip.GetClipRect());
|
||||
bool useClipBounds = true;
|
||||
nsRect paintBounds = itemBounds;
|
||||
if (gfxPrefs::WebRenderBlobImages()) {
|
||||
paintBounds = itemBounds;
|
||||
useClipBounds = false;
|
||||
} else {
|
||||
paintBounds = aItem->GetClippedBounds(aDisplayListBuilder);
|
||||
}
|
||||
|
||||
// nsDisplayItem::Paint() may refer the variables that come from ComputeVisibility().
|
||||
// So we should call RecomputeVisibility() before painting. e.g.: nsDisplayBoxShadowInner
|
||||
// uses mVisibleRegion in Paint() and mVisibleRegion is computed in
|
||||
// nsDisplayBoxShadowInner::ComputeVisibility().
|
||||
nsRegion visibleRegion(clippedBounds);
|
||||
aItem->RecomputeVisibility(aDisplayListBuilder, &visibleRegion);
|
||||
nsRegion visibleRegion(itemBounds);
|
||||
aItem->RecomputeVisibility(aDisplayListBuilder, &visibleRegion, useClipBounds);
|
||||
|
||||
const int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
|
||||
LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(clippedBounds, appUnitsPerDevPixel);
|
||||
LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(paintBounds, appUnitsPerDevPixel);
|
||||
|
||||
gfx::Size scale = aSc.GetInheritedScale();
|
||||
// XXX not sure if paintSize should be in layer or layoutdevice pixels, it
|
||||
@ -502,7 +505,7 @@ WebRenderCommandBuilder::GenerateFallbackData(nsDisplayItem* aItem,
|
||||
nsRegion invalidRegion;
|
||||
|
||||
if (aItem->IsInvalid(invalid)) {
|
||||
invalidRegion.OrWith(clippedBounds);
|
||||
invalidRegion.OrWith(paintBounds);
|
||||
} else {
|
||||
nsPoint shift = itemBounds.TopLeft() - geometry->mBounds.TopLeft();
|
||||
geometry->MoveBy(shift);
|
||||
@ -511,9 +514,9 @@ WebRenderCommandBuilder::GenerateFallbackData(nsDisplayItem* aItem,
|
||||
nsRect lastBounds = fallbackData->GetBounds();
|
||||
lastBounds.MoveBy(shift);
|
||||
|
||||
if (!lastBounds.IsEqualInterior(clippedBounds)) {
|
||||
if (!lastBounds.IsEqualInterior(paintBounds)) {
|
||||
invalidRegion.OrWith(lastBounds);
|
||||
invalidRegion.OrWith(clippedBounds);
|
||||
invalidRegion.OrWith(paintBounds);
|
||||
}
|
||||
}
|
||||
needPaint = !invalidRegion.IsEmpty();
|
||||
@ -524,7 +527,7 @@ WebRenderCommandBuilder::GenerateFallbackData(nsDisplayItem* aItem,
|
||||
gfx::SurfaceFormat::A8 : gfx::SurfaceFormat::B8G8R8A8;
|
||||
if (gfxPrefs::WebRenderBlobImages()) {
|
||||
bool snapped;
|
||||
bool isOpaque = aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(clippedBounds);
|
||||
bool isOpaque = aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(paintBounds);
|
||||
|
||||
RefPtr<gfx::DrawEventRecorderMemory> recorder = MakeAndAddRef<gfx::DrawEventRecorderMemory>([&] (MemStream &aStream, std::vector<RefPtr<UnscaledFont>> &aUnscaledFonts) {
|
||||
size_t count = aUnscaledFonts.size();
|
||||
@ -585,7 +588,7 @@ WebRenderCommandBuilder::GenerateFallbackData(nsDisplayItem* aItem,
|
||||
|
||||
// Update current bounds to fallback data
|
||||
fallbackData->SetGeometry(Move(geometry));
|
||||
fallbackData->SetBounds(clippedBounds);
|
||||
fallbackData->SetBounds(paintBounds);
|
||||
|
||||
MOZ_ASSERT(fallbackData->GetKey());
|
||||
|
||||
|
@ -2776,8 +2776,11 @@ class CloneBufferObject : public NativeObject {
|
||||
}
|
||||
|
||||
auto buf = js::MakeUnique<JSStructuredCloneData>(0, 0, nbytes);
|
||||
if (!buf->Init(nbytes, nbytes))
|
||||
if (!buf || !buf->Init(nbytes, nbytes)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
js_memcpy(buf->Start(), data, nbytes);
|
||||
obj->discard();
|
||||
obj->setData(buf.release(), true);
|
||||
|
13
js/src/jit-test/tests/basic/bug1411294.js
Normal file
13
js/src/jit-test/tests/basic/bug1411294.js
Normal file
@ -0,0 +1,13 @@
|
||||
if (!('oomTest' in this))
|
||||
quit();
|
||||
oomTest(function() {
|
||||
eval(`var clonebuffer = serialize("abc");
|
||||
clonebuffer.clonebuffer = "\
|
||||
\\x00\\x00\\x00\\x00\\b\\x00\\xFF\\xFF\\f\
|
||||
\\x00\\x00\\x00\\x03\\x00\\xFF\\xFF\\x00\\x00\\x00\\x00\\x00\\x00\\x00\
|
||||
\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xF0?\\x00\\x00\\x00\\\x00\\x00\
|
||||
\\x00\\xFF\\xFF"
|
||||
var obj = deserialize(clonebuffer)
|
||||
assertEq(new ({ get }).keys(obj).toString(), "12,ab");
|
||||
`);
|
||||
});
|
@ -8,7 +8,7 @@ g.eval("(" + function() {
|
||||
dbg.onEnterFrame = undefined;
|
||||
count++;
|
||||
var ex = frame.eval("this").throw.unsafeDereference();
|
||||
assertEq(ex.message.includes("uninitialized"), true);
|
||||
assertEq(ex.message.includes("call super constructor"), true);
|
||||
assertEq(ex.message.includes("Foo2"), true);
|
||||
}
|
||||
} + ")()");
|
||||
|
@ -1027,7 +1027,7 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
|
||||
op = JSOp(*pc);
|
||||
}
|
||||
|
||||
uint32_t pcOff = script->pcToOffset(pc);
|
||||
const uint32_t pcOff = script->pcToOffset(pc);
|
||||
BaselineScript* baselineScript = script->baselineScript();
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -1107,8 +1107,13 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
|
||||
ICFallbackStub* fallbackStub = icEntry.firstStub()->getChainFallback();
|
||||
MOZ_ASSERT(fallbackStub->isMonitoredFallback());
|
||||
JitSpew(JitSpew_BaselineBailouts, " [TYPE-MONITOR CHAIN]");
|
||||
ICMonitoredFallbackStub* monFallbackStub = fallbackStub->toMonitoredFallbackStub();
|
||||
ICStub* firstMonStub = monFallbackStub->fallbackMonitorStub()->firstMonitorStub();
|
||||
|
||||
ICTypeMonitor_Fallback* typeMonitorFallback =
|
||||
fallbackStub->toMonitoredFallbackStub()->getFallbackMonitorStub(cx, script);
|
||||
if (!typeMonitorFallback)
|
||||
return false;
|
||||
|
||||
ICStub* firstMonStub = typeMonitorFallback->firstMonitorStub();
|
||||
|
||||
// To enter a monitoring chain, we load the top stack value into R0
|
||||
JitSpew(JitSpew_BaselineBailouts, " Popping top stack value into R0.");
|
||||
@ -1387,6 +1392,14 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
|
||||
if (!builder.writeWord(baselineStubFrameDescr, "Descriptor"))
|
||||
return false;
|
||||
|
||||
// Ensure we have a TypeMonitor fallback stub so we don't crash in JIT code
|
||||
// when we try to enter it. See callers of offsetOfFallbackMonitorStub.
|
||||
if (CodeSpec[*pc].format & JOF_TYPESET) {
|
||||
ICFallbackStub* fallbackStub = icEntry.firstStub()->getChainFallback();
|
||||
if (!fallbackStub->toMonitoredFallbackStub()->getFallbackMonitorStub(cx, script))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Push return address into ICCall_Scripted stub, immediately after the call.
|
||||
void* baselineCallReturnAddr = GetStubReturnAddress(cx, pc);
|
||||
MOZ_ASSERT(baselineCallReturnAddr);
|
||||
|
@ -2262,8 +2262,13 @@ js::jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
|
||||
return newStub;
|
||||
}
|
||||
case BaselineCacheIRStubKind::Monitored: {
|
||||
ICStub* monitorStub =
|
||||
stub->toMonitoredFallbackStub()->fallbackMonitorStub()->firstMonitorStub();
|
||||
ICTypeMonitor_Fallback* typeMonitorFallback =
|
||||
stub->toMonitoredFallbackStub()->getFallbackMonitorStub(cx, outerScript);
|
||||
if (!typeMonitorFallback) {
|
||||
cx->recoverFromOutOfMemory();
|
||||
return nullptr;
|
||||
}
|
||||
ICStub* monitorStub = typeMonitorFallback->firstMonitorStub();
|
||||
auto newStub = new(newStubMem) ICCacheIR_Monitored(code, monitorStub, stubInfo);
|
||||
writer.copyStubData(newStub->stubDataStart());
|
||||
stub->addNewStub(newStub);
|
||||
|
@ -982,24 +982,22 @@ BaselineCompiler::emitBody()
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fully sync the stack if there are incoming jumps.
|
||||
if (info->jumpTarget) {
|
||||
// Fully sync the stack if there are incoming jumps.
|
||||
frame.syncStack(0);
|
||||
frame.setStackDepth(info->stackDepth);
|
||||
masm.bind(labelOf(pc));
|
||||
} else if (MOZ_UNLIKELY(compileDebugInstrumentation_)) {
|
||||
// Also fully sync the stack if the debugger is enabled.
|
||||
frame.syncStack(0);
|
||||
} else {
|
||||
// At the beginning of any op, at most the top 2 stack-values are unsynced.
|
||||
if (frame.stackDepth() > 2)
|
||||
frame.syncStack(2);
|
||||
}
|
||||
|
||||
// Always sync in debug mode.
|
||||
if (compileDebugInstrumentation_)
|
||||
frame.syncStack(0);
|
||||
|
||||
// At the beginning of any op, at most the top 2 stack-values are unsynced.
|
||||
if (frame.stackDepth() > 2)
|
||||
frame.syncStack(2);
|
||||
|
||||
frame.assertValidState(*info);
|
||||
|
||||
masm.bind(labelOf(pc));
|
||||
|
||||
// Add a PC -> native mapping entry for the current op. These entries are
|
||||
// used when we need the native code address for a given pc, for instance
|
||||
// for bailouts from Ion, the debugger and exception handling. See
|
||||
@ -1007,13 +1005,13 @@ BaselineCompiler::emitBody()
|
||||
bool addIndexEntry = (pc == script->code() || lastOpUnreachable || emittedOps > 100);
|
||||
if (addIndexEntry)
|
||||
emittedOps = 0;
|
||||
if (!addPCMappingEntry(addIndexEntry)) {
|
||||
if (MOZ_UNLIKELY(!addPCMappingEntry(addIndexEntry))) {
|
||||
ReportOutOfMemory(cx);
|
||||
return Method_Error;
|
||||
}
|
||||
|
||||
// Emit traps for breakpoints and step mode.
|
||||
if (compileDebugInstrumentation_ && !emitDebugTrap())
|
||||
if (MOZ_UNLIKELY(compileDebugInstrumentation_) && !emitDebugTrap())
|
||||
return Method_Error;
|
||||
|
||||
switch (op) {
|
||||
@ -1033,7 +1031,7 @@ BaselineCompiler::emitBody()
|
||||
|
||||
#define EMIT_OP(OP) \
|
||||
case OP: \
|
||||
if (!this->emit_##OP()) \
|
||||
if (MOZ_UNLIKELY(!this->emit_##OP())) \
|
||||
return Method_Error; \
|
||||
break;
|
||||
OPCODE_LIST(EMIT_OP)
|
||||
|
@ -757,7 +757,10 @@ CloneOldBaselineStub(JSContext* cx, DebugModeOSREntryVector& entries, size_t ent
|
||||
ICStub* firstMonitorStub;
|
||||
if (fallbackStub->isMonitoredFallback()) {
|
||||
ICMonitoredFallbackStub* monitored = fallbackStub->toMonitoredFallbackStub();
|
||||
firstMonitorStub = monitored->fallbackMonitorStub()->firstMonitorStub();
|
||||
ICTypeMonitor_Fallback* fallback = monitored->getFallbackMonitorStub(cx, entry.script);
|
||||
if (!fallback)
|
||||
return false;
|
||||
firstMonitorStub = fallback->firstMonitorStub();
|
||||
} else {
|
||||
firstMonitorStub = nullptr;
|
||||
}
|
||||
|
@ -1841,7 +1841,8 @@ ICSetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm, Handle<
|
||||
|
||||
static bool
|
||||
TryAttachFunApplyStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsbytecode* pc,
|
||||
HandleValue thisv, uint32_t argc, Value* argv, bool* attached)
|
||||
HandleValue thisv, uint32_t argc, Value* argv,
|
||||
ICTypeMonitor_Fallback* typeMonitorFallback, bool* attached)
|
||||
{
|
||||
if (argc != 2)
|
||||
return true;
|
||||
@ -1858,7 +1859,7 @@ TryAttachFunApplyStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
|
||||
JitSpew(JitSpew_BaselineIC, " Generating Call_ScriptedApplyArguments stub");
|
||||
|
||||
ICCall_ScriptedApplyArguments::Compiler compiler(
|
||||
cx, stub->fallbackMonitorStub()->firstMonitorStub(), script->pcToOffset(pc));
|
||||
cx, typeMonitorFallback->firstMonitorStub(), script->pcToOffset(pc));
|
||||
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
return false;
|
||||
@ -1876,7 +1877,7 @@ TryAttachFunApplyStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
|
||||
JitSpew(JitSpew_BaselineIC, " Generating Call_ScriptedApplyArray stub");
|
||||
|
||||
ICCall_ScriptedApplyArray::Compiler compiler(
|
||||
cx, stub->fallbackMonitorStub()->firstMonitorStub(), script->pcToOffset(pc));
|
||||
cx, typeMonitorFallback->firstMonitorStub(), script->pcToOffset(pc));
|
||||
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
return false;
|
||||
@ -1891,7 +1892,8 @@ TryAttachFunApplyStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
|
||||
|
||||
static bool
|
||||
TryAttachFunCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsbytecode* pc,
|
||||
HandleValue thisv, bool* attached)
|
||||
HandleValue thisv, ICTypeMonitor_Fallback* typeMonitorFallback,
|
||||
bool* attached)
|
||||
{
|
||||
// Try to attach a stub for Function.prototype.call with scripted |this|.
|
||||
|
||||
@ -1908,7 +1910,7 @@ TryAttachFunCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
|
||||
{
|
||||
JitSpew(JitSpew_BaselineIC, " Generating Call_ScriptedFunCall stub");
|
||||
|
||||
ICCall_ScriptedFunCall::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
ICCall_ScriptedFunCall::Compiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
|
||||
script->pcToOffset(pc));
|
||||
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
@ -2155,6 +2157,10 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
|
||||
if (!callee.isObject())
|
||||
return true;
|
||||
|
||||
ICTypeMonitor_Fallback* typeMonitorFallback = stub->getFallbackMonitorStub(cx, script);
|
||||
if (!typeMonitorFallback)
|
||||
return false;
|
||||
|
||||
RootedObject obj(cx, &callee.toObject());
|
||||
if (!obj->is<JSFunction>()) {
|
||||
// Try to attach a stub for a call/construct hook on the object.
|
||||
@ -2169,7 +2175,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
|
||||
return false;
|
||||
|
||||
JitSpew(JitSpew_BaselineIC, " Generating Call_ClassHook stub");
|
||||
ICCall_ClassHook::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
ICCall_ClassHook::Compiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
|
||||
obj->getClass(), hook, templateObject,
|
||||
script->pcToOffset(pc), constructing);
|
||||
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
@ -2217,7 +2223,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
|
||||
// Create a Call_AnyScripted stub.
|
||||
JitSpew(JitSpew_BaselineIC, " Generating Call_AnyScripted stub (cons=%s, spread=%s)",
|
||||
constructing ? "yes" : "no", isSpread ? "yes" : "no");
|
||||
ICCallScriptedCompiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
ICCallScriptedCompiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
|
||||
constructing, isSpread, script->pcToOffset(pc));
|
||||
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
@ -2286,7 +2292,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
|
||||
" Generating Call_Scripted stub (fun=%p, %s:%zu, cons=%s, spread=%s)",
|
||||
fun.get(), fun->nonLazyScript()->filename(), fun->nonLazyScript()->lineno(),
|
||||
constructing ? "yes" : "no", isSpread ? "yes" : "no");
|
||||
ICCallScriptedCompiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
ICCallScriptedCompiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
|
||||
fun, templateObject,
|
||||
constructing, isSpread, script->pcToOffset(pc));
|
||||
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
@ -2304,8 +2310,10 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
|
||||
|
||||
// Check for JSOP_FUNAPPLY
|
||||
if (op == JSOP_FUNAPPLY) {
|
||||
if (fun->native() == fun_apply)
|
||||
return TryAttachFunApplyStub(cx, stub, script, pc, thisv, argc, vp + 2, handled);
|
||||
if (fun->native() == fun_apply) {
|
||||
return TryAttachFunApplyStub(cx, stub, script, pc, thisv, argc, vp + 2,
|
||||
typeMonitorFallback, handled);
|
||||
}
|
||||
|
||||
// Don't try to attach a "regular" optimized call stubs for FUNAPPLY ops,
|
||||
// since MagicArguments may escape through them.
|
||||
@ -2313,7 +2321,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
|
||||
}
|
||||
|
||||
if (op == JSOP_FUNCALL && fun->native() == fun_call) {
|
||||
if (!TryAttachFunCallStub(cx, stub, script, pc, thisv, handled))
|
||||
if (!TryAttachFunCallStub(cx, stub, script, pc, thisv, typeMonitorFallback, handled))
|
||||
return false;
|
||||
if (*handled)
|
||||
return true;
|
||||
@ -2362,7 +2370,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
|
||||
|
||||
JitSpew(JitSpew_BaselineIC, " Generating Call_Native stub (fun=%p, cons=%s, spread=%s)",
|
||||
fun.get(), constructing ? "yes" : "no", isSpread ? "yes" : "no");
|
||||
ICCall_Native::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
ICCall_Native::Compiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
|
||||
fun, templateObject, constructing, ignoresReturnValue,
|
||||
isSpread, script->pcToOffset(pc));
|
||||
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
@ -2430,7 +2438,11 @@ TryAttachConstStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript scr
|
||||
arrObj->setDenseElementWithType(cx, i, StringValue(str));
|
||||
}
|
||||
|
||||
ICCall_ConstStringSplit::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
ICTypeMonitor_Fallback* typeMonitorFallback = stub->getFallbackMonitorStub(cx, script);
|
||||
if (!typeMonitorFallback)
|
||||
return false;
|
||||
|
||||
ICCall_ConstStringSplit::Compiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
|
||||
script->pcToOffset(pc), str, sep, arrObj);
|
||||
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
@ -3027,7 +3039,9 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
// At this point, ICStubReg points to the ICCall_Fallback stub, which is NOT
|
||||
// a MonitoredStub, but rather a MonitoredFallbackStub. To use EmitEnterTypeMonitorIC,
|
||||
// first load the ICTypeMonitor_Fallback stub into ICStubReg. Then, use
|
||||
// EmitEnterTypeMonitorIC with a custom struct offset.
|
||||
// EmitEnterTypeMonitorIC with a custom struct offset. Note that we must
|
||||
// have a non-null fallbackMonitorStub here because InitFromBailout
|
||||
// delazifies.
|
||||
masm.loadPtr(Address(ICStubReg, ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()),
|
||||
ICStubReg);
|
||||
EmitEnterTypeMonitorIC(masm, ICTypeMonitor_Fallback::offsetOfFirstMonitorStub());
|
||||
|
@ -431,12 +431,7 @@ class ICGetElem_Fallback : public ICMonitoredFallbackStub
|
||||
{ }
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
ICGetElem_Fallback* stub = newStub<ICGetElem_Fallback>(space, getStubCode());
|
||||
if (!stub)
|
||||
return nullptr;
|
||||
if (!stub->initMonitoringChain(cx, space))
|
||||
return nullptr;
|
||||
return stub;
|
||||
return newStub<ICGetElem_Fallback>(space, getStubCode());
|
||||
}
|
||||
};
|
||||
};
|
||||
@ -562,10 +557,7 @@ class ICGetName_Fallback : public ICMonitoredFallbackStub
|
||||
{ }
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
ICGetName_Fallback* stub = newStub<ICGetName_Fallback>(space, getStubCode());
|
||||
if (!stub || !stub->initMonitoringChain(cx, space))
|
||||
return nullptr;
|
||||
return stub;
|
||||
return newStub<ICGetName_Fallback>(space, getStubCode());
|
||||
}
|
||||
};
|
||||
};
|
||||
@ -617,11 +609,7 @@ class ICGetIntrinsic_Fallback : public ICMonitoredFallbackStub
|
||||
{ }
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
ICGetIntrinsic_Fallback* stub =
|
||||
newStub<ICGetIntrinsic_Fallback>(space, getStubCode());
|
||||
if (!stub || !stub->initMonitoringChain(cx, space))
|
||||
return nullptr;
|
||||
return stub;
|
||||
return newStub<ICGetIntrinsic_Fallback>(space, getStubCode());
|
||||
}
|
||||
};
|
||||
};
|
||||
@ -799,10 +787,7 @@ class ICCall_Fallback : public ICMonitoredFallbackStub
|
||||
{ }
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
ICCall_Fallback* stub = newStub<ICCall_Fallback>(space, getStubCode());
|
||||
if (!stub || !stub->initMonitoringChain(cx, space))
|
||||
return nullptr;
|
||||
return stub;
|
||||
return newStub<ICCall_Fallback>(space, getStubCode());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -1079,8 +1079,9 @@ BaselineScript::purgeOptimizedStubs(Zone* zone)
|
||||
// Monitor stubs can't make calls, so are always in the
|
||||
// optimized stub space.
|
||||
ICTypeMonitor_Fallback* lastMonStub =
|
||||
lastStub->toMonitoredFallbackStub()->fallbackMonitorStub();
|
||||
lastMonStub->resetMonitorStubChain(zone);
|
||||
lastStub->toMonitoredFallbackStub()->maybeFallbackMonitorStub();
|
||||
if (lastMonStub)
|
||||
lastMonStub->resetMonitorStubChain(zone);
|
||||
}
|
||||
} else if (lastStub->isTypeMonitor_Fallback()) {
|
||||
lastStub->toTypeMonitor_Fallback()->resetMonitorStubChain(zone);
|
||||
|
@ -1255,14 +1255,8 @@ IonScript::purgeOptimizedStubs(Zone* zone)
|
||||
|
||||
lastStub->toFallbackStub()->setInvalid();
|
||||
|
||||
if (lastStub->isMonitoredFallback()) {
|
||||
// Monitor stubs can't make calls, so are always in the
|
||||
// optimized stub space.
|
||||
ICTypeMonitor_Fallback* lastMonStub =
|
||||
lastStub->toMonitoredFallbackStub()->fallbackMonitorStub();
|
||||
lastMonStub->resetMonitorStubChain(zone);
|
||||
lastMonStub->setInvalid();
|
||||
}
|
||||
MOZ_ASSERT(!lastStub->isMonitoredFallback(),
|
||||
"None of the shared stubs used in Ion are monitored");
|
||||
} else if (lastStub->isTypeMonitor_Fallback()) {
|
||||
lastStub->toTypeMonitor_Fallback()->resetMonitorStubChain(zone);
|
||||
lastStub->toTypeMonitor_Fallback()->setInvalid();
|
||||
|
@ -226,10 +226,16 @@ ICStub::trace(JSTracer* trc)
|
||||
// because the regular monitored stubs will always have a monitored fallback stub
|
||||
// that references the same stub chain.
|
||||
if (isMonitoredFallback()) {
|
||||
ICTypeMonitor_Fallback* lastMonStub = toMonitoredFallbackStub()->fallbackMonitorStub();
|
||||
for (ICStubConstIterator iter(lastMonStub->firstMonitorStub()); !iter.atEnd(); iter++) {
|
||||
MOZ_ASSERT_IF(iter->next() == nullptr, *iter == lastMonStub);
|
||||
iter->trace(trc);
|
||||
ICTypeMonitor_Fallback* lastMonStub =
|
||||
toMonitoredFallbackStub()->maybeFallbackMonitorStub();
|
||||
if (lastMonStub) {
|
||||
for (ICStubConstIterator iter(lastMonStub->firstMonitorStub());
|
||||
!iter.atEnd();
|
||||
iter++)
|
||||
{
|
||||
MOZ_ASSERT_IF(iter->next() == nullptr, *iter == lastMonStub);
|
||||
iter->trace(trc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -366,7 +372,9 @@ ICFallbackStub::unlinkStub(Zone* zone, ICStub* prev, ICStub* stub)
|
||||
// We just have to reset its firstMonitorStub_ field to avoid a stale
|
||||
// pointer when purgeOptimizedStubs destroys all optimized monitor
|
||||
// stubs (unlinked stubs won't be updated).
|
||||
ICTypeMonitor_Fallback* monitorFallback = toMonitoredFallbackStub()->fallbackMonitorStub();
|
||||
ICTypeMonitor_Fallback* monitorFallback =
|
||||
toMonitoredFallbackStub()->maybeFallbackMonitorStub();
|
||||
MOZ_ASSERT(monitorFallback);
|
||||
stub->toMonitoredStub()->resetFirstMonitorStub(monitorFallback);
|
||||
}
|
||||
|
||||
@ -457,11 +465,12 @@ ICMonitoredStub::ICMonitoredStub(Kind kind, JitCode* stubCode, ICStub* firstMoni
|
||||
}
|
||||
|
||||
bool
|
||||
ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx, ICStubSpace* space)
|
||||
ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx, JSScript* script)
|
||||
{
|
||||
MOZ_ASSERT(fallbackMonitorStub_ == nullptr);
|
||||
|
||||
ICTypeMonitor_Fallback::Compiler compiler(cx, this);
|
||||
ICStubSpace* space = script->baselineScript()->fallbackStubSpace();
|
||||
ICTypeMonitor_Fallback* stub = compiler.getStub(space);
|
||||
if (!stub)
|
||||
return false;
|
||||
@ -473,7 +482,10 @@ bool
|
||||
ICMonitoredFallbackStub::addMonitorStubForValue(JSContext* cx, BaselineFrame* frame,
|
||||
StackTypeSet* types, HandleValue val)
|
||||
{
|
||||
return fallbackMonitorStub_->addMonitorStubForValue(cx, frame, types, val);
|
||||
ICTypeMonitor_Fallback* typeMonitorFallback = getFallbackMonitorStub(cx, frame->script());
|
||||
if (!typeMonitorFallback)
|
||||
return false;
|
||||
return typeMonitorFallback->addMonitorStubForValue(cx, frame, types, val);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -2162,7 +2174,8 @@ ICGetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
|
||||
// When we get here, ICStubReg contains the ICGetProp_Fallback stub,
|
||||
// which we can't use to enter the TypeMonitor IC, because it's a MonitoredFallbackStub
|
||||
// instead of a MonitoredStub. So, we cheat.
|
||||
// instead of a MonitoredStub. So, we cheat. Note that we must have a
|
||||
// non-null fallbackMonitorStub here because InitFromBailout delazifies.
|
||||
masm.loadPtr(Address(ICStubReg, ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()),
|
||||
ICStubReg);
|
||||
EmitEnterTypeMonitorIC(masm, ICTypeMonitor_Fallback::offsetOfFirstMonitorStub());
|
||||
|
@ -1201,7 +1201,8 @@ class SharedStubInfo
|
||||
class ICMonitoredFallbackStub : public ICFallbackStub
|
||||
{
|
||||
protected:
|
||||
// Pointer to the fallback monitor stub.
|
||||
// Pointer to the fallback monitor stub. Created lazily by
|
||||
// getFallbackMonitorStub if needed.
|
||||
ICTypeMonitor_Fallback* fallbackMonitorStub_;
|
||||
|
||||
ICMonitoredFallbackStub(Kind kind, JitCode* stubCode)
|
||||
@ -1209,11 +1210,17 @@ class ICMonitoredFallbackStub : public ICFallbackStub
|
||||
fallbackMonitorStub_(nullptr) {}
|
||||
|
||||
public:
|
||||
MOZ_MUST_USE bool initMonitoringChain(JSContext* cx, ICStubSpace* space);
|
||||
MOZ_MUST_USE bool initMonitoringChain(JSContext* cx, JSScript* script);
|
||||
MOZ_MUST_USE bool addMonitorStubForValue(JSContext* cx, BaselineFrame* frame,
|
||||
StackTypeSet* types, HandleValue val);
|
||||
|
||||
inline ICTypeMonitor_Fallback* fallbackMonitorStub() const {
|
||||
ICTypeMonitor_Fallback* maybeFallbackMonitorStub() const {
|
||||
return fallbackMonitorStub_;
|
||||
}
|
||||
ICTypeMonitor_Fallback* getFallbackMonitorStub(JSContext* cx, JSScript* script) {
|
||||
if (!fallbackMonitorStub_ && !initMonitoringChain(cx, script))
|
||||
return nullptr;
|
||||
MOZ_ASSERT(fallbackMonitorStub_);
|
||||
return fallbackMonitorStub_;
|
||||
}
|
||||
|
||||
@ -2361,10 +2368,7 @@ class ICGetProp_Fallback : public ICMonitoredFallbackStub
|
||||
{ }
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
ICGetProp_Fallback* stub = newStub<ICGetProp_Fallback>(space, getStubCode());
|
||||
if (!stub || !stub->initMonitoringChain(cx, space))
|
||||
return nullptr;
|
||||
return stub;
|
||||
return newStub<ICGetProp_Fallback>(space, getStubCode());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -102,8 +102,8 @@ MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE, 0, JSEXN_TYPEERR, "can't set prototype: i
|
||||
MSG_DEF(JSMSG_INVALID_ARG_TYPE, 3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
|
||||
MSG_DEF(JSMSG_TERMINATED, 1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
|
||||
MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
|
||||
MSG_DEF(JSMSG_UNINITIALIZED_THIS, 1, JSEXN_REFERENCEERR, "|this| used uninitialized in {0} class constructor")
|
||||
MSG_DEF(JSMSG_UNINITIALIZED_THIS_ARROW, 0, JSEXN_REFERENCEERR, "|this| used uninitialized in arrow function in class constructor")
|
||||
MSG_DEF(JSMSG_UNINITIALIZED_THIS, 1, JSEXN_REFERENCEERR, "must call super constructor before using |this| in {0} class constructor")
|
||||
MSG_DEF(JSMSG_UNINITIALIZED_THIS_ARROW, 0, JSEXN_REFERENCEERR, "must call super constructor before using |this| in arrow function in derived class constructor")
|
||||
MSG_DEF(JSMSG_BAD_DERIVED_RETURN, 1, JSEXN_TYPEERR, "derived class constructor returned invalid value {0}")
|
||||
MSG_DEF(JSMSG_BAD_HERITAGE, 2, JSEXN_TYPEERR, "class heritage {0} is {1}")
|
||||
MSG_DEF(JSMSG_NOT_OBJORNULL, 1, JSEXN_TYPEERR, "{0} is not an object or null")
|
||||
|
@ -5669,7 +5669,7 @@ struct BufferStreamState
|
||||
}
|
||||
};
|
||||
|
||||
ExclusiveWaitableData<BufferStreamState> bufferStreamState(mutexid::BufferStreamState);
|
||||
static ExclusiveWaitableData<BufferStreamState> bufferStreamState(mutexid::BufferStreamState);
|
||||
|
||||
static void
|
||||
BufferStreamMain(BufferStreamJob* job)
|
||||
@ -5746,15 +5746,6 @@ ConsumeBufferSource(JSContext* cx, JS::HandleObject obj, JS::MimeType, JS::Strea
|
||||
return jobPtr->thread.init(BufferStreamMain, jobPtr);
|
||||
}
|
||||
|
||||
static void
|
||||
ShutdownBufferStreams()
|
||||
{
|
||||
auto state = bufferStreamState.lock();
|
||||
state->shutdown = true;
|
||||
while (!state->jobs.empty())
|
||||
state.wait(/* jobs empty */);
|
||||
}
|
||||
|
||||
static bool
|
||||
SetBufferStreamParams(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@ -8633,6 +8624,13 @@ main(int argc, char** argv, char** envp)
|
||||
{
|
||||
PreInit();
|
||||
|
||||
auto shutdownBufferStreams = MakeScopeExit([] {
|
||||
auto state = bufferStreamState.lock();
|
||||
state->shutdown = true;
|
||||
while (!state->jobs.empty())
|
||||
state.wait(/* jobs empty */);
|
||||
});
|
||||
|
||||
sArgc = argc;
|
||||
sArgv = argv;
|
||||
|
||||
@ -8975,7 +8973,6 @@ main(int argc, char** argv, char** envp)
|
||||
|
||||
KillWorkerThreads(cx);
|
||||
|
||||
ShutdownBufferStreams();
|
||||
DestructSharedArrayBufferMailbox();
|
||||
|
||||
JS_DestroyContext(cx);
|
||||
|
@ -1,9 +1,9 @@
|
||||
function checkErr(f, className) {
|
||||
var expected;
|
||||
if (className !== "")
|
||||
expected = "|this| used uninitialized in " + className + " class constructor";
|
||||
expected = "must call super constructor before using |this| in " + className + " class constructor";
|
||||
else
|
||||
expected = "|this| used uninitialized in arrow function in class constructor";
|
||||
expected = "must call super constructor before using |this| in arrow function in derived class constructor";
|
||||
try {
|
||||
f();
|
||||
assertEq(0, 1);
|
||||
|
@ -2929,7 +2929,8 @@ nsDisplayItem::ComputeVisibility(nsDisplayListBuilder* aBuilder,
|
||||
|
||||
bool
|
||||
nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
|
||||
nsRegion* aVisibleRegion) {
|
||||
nsRegion* aVisibleRegion,
|
||||
bool aUseClipBounds) {
|
||||
if (mForceNotVisible && !GetSameCoordinateSystemChildren()) {
|
||||
// mForceNotVisible wants to ensure that this display item doesn't render
|
||||
// anything itself. If this item has contents, then we obviously want to
|
||||
@ -2937,7 +2938,13 @@ nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
|
||||
NS_ASSERTION(mVisibleRect.IsEmpty(),
|
||||
"invisible items without children should have empty vis rect");
|
||||
} else {
|
||||
nsRect bounds = GetClippedBounds(aBuilder);
|
||||
nsRect bounds;
|
||||
if (aUseClipBounds) {
|
||||
bounds = GetClippedBounds(aBuilder);
|
||||
} else {
|
||||
bool snap;
|
||||
bounds = GetBounds(aBuilder, &snap);
|
||||
}
|
||||
|
||||
nsRegion itemVisible;
|
||||
itemVisible.And(*aVisibleRegion, bounds);
|
||||
|
@ -2505,7 +2505,8 @@ public:
|
||||
* -- Subtracts bounds from aVisibleRegion if the item is opaque
|
||||
*/
|
||||
bool RecomputeVisibility(nsDisplayListBuilder* aBuilder,
|
||||
nsRegion* aVisibleRegion);
|
||||
nsRegion* aVisibleRegion,
|
||||
bool aUseClipBounds = true);
|
||||
|
||||
/**
|
||||
* Returns the result of aBuilder->ToReferenceFrame(GetUnderlyingFrame())
|
||||
|
@ -1,112 +1,11 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "nsBufferedStreams.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "Helpers.h"
|
||||
|
||||
class AsyncStringStream final : public nsIAsyncInputStream
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> mStream;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
explicit AsyncStringStream(const nsACString& aBuffer)
|
||||
{
|
||||
NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Available(uint64_t* aLength) override
|
||||
{
|
||||
return mStream->Available(aLength);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override
|
||||
{
|
||||
return mStream->Read(aBuffer, aCount, aReadCount);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
|
||||
uint32_t aCount, uint32_t *aResult) override
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Close() override
|
||||
{
|
||||
nsresult rv = mStream->Close();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
MaybeExecCallback(mCallback, mCallbackEventTarget);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
IsNonBlocking(bool* aNonBlocking) override
|
||||
{
|
||||
return mStream->IsNonBlocking(aNonBlocking);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
CloseWithStatus(nsresult aStatus) override
|
||||
{
|
||||
return Close();
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
AsyncWait(nsIInputStreamCallback* aCallback,
|
||||
uint32_t aFlags, uint32_t aRequestedCount,
|
||||
nsIEventTarget* aEventTarget) override
|
||||
{
|
||||
if (aFlags & nsIAsyncInputStream::WAIT_CLOSURE_ONLY) {
|
||||
mCallback = aCallback;
|
||||
mCallbackEventTarget = aEventTarget;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MaybeExecCallback(aCallback, aEventTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MaybeExecCallback(nsIInputStreamCallback* aCallback,
|
||||
nsIEventTarget* aEventTarget)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStreamCallback> callback = aCallback;
|
||||
nsCOMPtr<nsIAsyncInputStream> self = this;
|
||||
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
||||
"AsyncWait", [callback, self]() { callback->OnInputStreamReady(self); });
|
||||
|
||||
if (aEventTarget) {
|
||||
aEventTarget->Dispatch(r.forget());
|
||||
} else {
|
||||
r->Run();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
~AsyncStringStream() = default;
|
||||
|
||||
nsCOMPtr<nsIInputStreamCallback> mCallback;
|
||||
nsCOMPtr<nsIEventTarget> mCallbackEventTarget;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(AsyncStringStream, nsIAsyncInputStream, nsIInputStream)
|
||||
|
||||
// Helper function for creating a AsyncStringStream
|
||||
// Helper function for creating a testing::AsyncStringStream
|
||||
already_AddRefed<nsBufferedInputStream>
|
||||
CreateStream(uint32_t aSize, nsCString& aBuffer)
|
||||
{
|
||||
@ -115,7 +14,7 @@ CreateStream(uint32_t aSize, nsCString& aBuffer)
|
||||
aBuffer.BeginWriting()[i] = i % 10;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream = new AsyncStringStream(aBuffer);
|
||||
nsCOMPtr<nsIInputStream> stream = new testing::AsyncStringStream(aBuffer);
|
||||
|
||||
RefPtr<nsBufferedInputStream> bis = new nsBufferedInputStream();
|
||||
bis->Init(stream, aSize);
|
||||
|
@ -17,10 +17,12 @@ const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
Cu.import("resource://gre/modules/AsyncShutdown.jsm", this);
|
||||
Cu.import("resource://gre/modules/DownloadPaths.jsm", this);
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/osfile.jsm", this);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
Cu.import("resource://testing-common/Assert.jsm", this);
|
||||
|
||||
let gFileCounter = 1;
|
||||
let gPathsToRemove = [];
|
||||
|
||||
this.FileTestUtils = {
|
||||
/**
|
||||
@ -50,25 +52,84 @@ this.FileTestUtils = {
|
||||
let file = this._globalTemporaryDirectory.clone();
|
||||
file.append(leafName);
|
||||
Assert.ok(!file.exists(), "Sanity check the temporary file doesn't exist.");
|
||||
|
||||
// Since directory iteration on Windows may not see files that have just
|
||||
// been created, keep track of the known file names to be removed.
|
||||
gPathsToRemove.push(file.path);
|
||||
return file;
|
||||
},
|
||||
|
||||
/**
|
||||
* Attemps to remove the given file or directory recursively, in a way that
|
||||
* works even on Windows, where race conditions may occur in the file system
|
||||
* when creating and removing files at the pace of the test suites.
|
||||
*
|
||||
* The function may fail silently if access is denied. This means that it
|
||||
* should only be used to clean up temporary files, rather than for cases
|
||||
* where the removal is part of a test and must be guaranteed.
|
||||
*
|
||||
* @param path
|
||||
* String representing the path to remove.
|
||||
* @param isDir [optional]
|
||||
* Indicates whether this is a directory. If not specified, this will
|
||||
* be determined by querying the file system.
|
||||
*/
|
||||
async tolerantRemove(path, isDir) {
|
||||
try {
|
||||
if (isDir === undefined) {
|
||||
isDir = (await OS.File.stat(path)).isDir;
|
||||
}
|
||||
if (isDir) {
|
||||
// The test created a directory, remove its contents recursively. The
|
||||
// deletion may still fail with an access denied error on Windows if any
|
||||
// of the files in the folder were recently deleted.
|
||||
await OS.File.removeDir(path);
|
||||
} else {
|
||||
// This is the usual case of a test file that has to be removed.
|
||||
await OS.File.remove(path);
|
||||
}
|
||||
} catch (ex) {
|
||||
// On Windows, we may get an access denied error instead of a no such file
|
||||
// error if the file existed before, and was recently deleted. There is no
|
||||
// way to distinguish this from an access list issue because checking for
|
||||
// the file existence would also result in the same error.
|
||||
if (!(ex instanceof OS.File.Error) ||
|
||||
!(ex.becauseNoSuchFile || ex.becauseAccessDenied)) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a reference to a global temporary directory that will be deleted
|
||||
* when all tests terminate.
|
||||
*/
|
||||
XPCOMUtils.defineLazyGetter(FileTestUtils, "_globalTemporaryDirectory", () => {
|
||||
// While previous test runs should have deleted their temporary directories,
|
||||
// on Windows they might still be pending deletion on the physical file
|
||||
// system. This makes a simple nsIFile.createUnique call unreliable, and we
|
||||
// have to use a random number to make a collision unlikely.
|
||||
let randomNumber = Math.floor(Math.random() * 1000000);
|
||||
let dir = FileUtils.getFile("TmpD", ["testdir-" + randomNumber]);
|
||||
dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
AsyncShutdown.profileBeforeChange.addBlocker("Removing test files", () => {
|
||||
// Note that this may fail if any test leaves inaccessible files behind.
|
||||
dir.remove(true);
|
||||
XPCOMUtils.defineLazyGetter(FileTestUtils, "_globalTemporaryDirectory",
|
||||
function() {
|
||||
// While previous test runs should have deleted their temporary directories,
|
||||
// on Windows they might still be pending deletion on the physical file
|
||||
// system. This makes a simple nsIFile.createUnique call unreliable, and we
|
||||
// have to use a random number to make a collision unlikely.
|
||||
let randomNumber = Math.floor(Math.random() * 1000000);
|
||||
let dir = FileUtils.getFile("TmpD", ["testdir-" + randomNumber]);
|
||||
dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
AsyncShutdown.profileBeforeChange.addBlocker("Removing test files",
|
||||
async () => {
|
||||
// Remove the files we know about first.
|
||||
for (let path of gPathsToRemove) {
|
||||
await this.tolerantRemove(path);
|
||||
}
|
||||
// Detect any extra files, like the ".part" files of downloads.
|
||||
let iterator = new OS.File.DirectoryIterator(dir.path);
|
||||
try {
|
||||
await iterator.forEach(entry => this.tolerantRemove(entry.path,
|
||||
entry.isDir));
|
||||
} finally {
|
||||
iterator.close();
|
||||
}
|
||||
// This will fail if any test leaves inaccessible files behind.
|
||||
await OS.File.removeEmptyDir(dir.path);
|
||||
});
|
||||
return dir;
|
||||
});
|
||||
return dir;
|
||||
});
|
||||
|
@ -997,7 +997,7 @@ function getDefaultFileName(aDefaultFileName, aURI, aDocument,
|
||||
}
|
||||
}
|
||||
if (fileName)
|
||||
return fileName;
|
||||
return validateFileName(fileName);
|
||||
}
|
||||
|
||||
let docTitle;
|
||||
@ -1043,7 +1043,7 @@ function getDefaultFileName(aDefaultFileName, aURI, aDocument,
|
||||
try {
|
||||
if (aURI.host)
|
||||
// 7) Use the host.
|
||||
return aURI.host;
|
||||
return validateFileName(aURI.host);
|
||||
} catch (e) {
|
||||
// Some files have no information at all, like Javascript generated pages
|
||||
}
|
||||
|
@ -207,7 +207,8 @@ nsUnknownContentTypeDialog.prototype = {
|
||||
bundle.GetStringFromName("badPermissions"));
|
||||
},
|
||||
|
||||
promptForSaveToFileAsync: function(aLauncher, aContext, aDefaultFile, aSuggestedFileExtension, aForcePrompt) {
|
||||
promptForSaveToFileAsync: function(aLauncher, aContext, aDefaultFileName,
|
||||
aSuggestedFileExtension, aForcePrompt) {
|
||||
var result = null;
|
||||
|
||||
this.mLauncher = aLauncher;
|
||||
@ -260,7 +261,8 @@ nsUnknownContentTypeDialog.prototype = {
|
||||
let defaultFolder = new FileUtils.File(preferredDir);
|
||||
|
||||
try {
|
||||
result = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExtension);
|
||||
result = this.validateLeafName(defaultFolder, aDefaultFileName,
|
||||
aSuggestedFileExtension);
|
||||
}
|
||||
catch (ex) {
|
||||
// When the default download directory is write-protected,
|
||||
@ -284,7 +286,9 @@ nsUnknownContentTypeDialog.prototype = {
|
||||
var picker = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
|
||||
var windowTitle = bundle.GetStringFromName("saveDialogTitle");
|
||||
picker.init(parent, windowTitle, nsIFilePicker.modeSave);
|
||||
picker.defaultString = aDefaultFile;
|
||||
if (aDefaultFileName) {
|
||||
picker.defaultString = this.getFinalLeafName(aDefaultFileName);
|
||||
}
|
||||
|
||||
if (aSuggestedFileExtension) {
|
||||
// aSuggestedFileExtension includes the period, so strip it
|
||||
|
@ -116,6 +116,11 @@ SlicedInputStream::Available(uint64_t* aLength)
|
||||
}
|
||||
|
||||
nsresult rv = mInputStream->Available(aLength);
|
||||
if (rv == NS_BASE_STREAM_CLOSED) {
|
||||
mClosed = true;
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -159,7 +164,12 @@ SlicedInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
|
||||
uint32_t bytesRead;
|
||||
uint64_t bufCount = XPCOM_MIN(mStart - mCurPos, (uint64_t)sizeof(buf));
|
||||
nsresult rv = mInputStream->Read(buf, bufCount, &bytesRead);
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || bytesRead == 0) {
|
||||
if (NS_SUCCEEDED(rv) && bytesRead == 0) {
|
||||
mClosed = true;
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -179,7 +189,12 @@ SlicedInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
|
||||
}
|
||||
|
||||
nsresult rv = mInputStream->Read(aBuffer, aCount, aReadCount);
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || *aReadCount == 0) {
|
||||
if (NS_SUCCEEDED(rv) && *aReadCount == 0) {
|
||||
mClosed = true;
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -309,12 +324,17 @@ SlicedInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
|
||||
uint32_t bytesRead;
|
||||
uint64_t bufCount = XPCOM_MIN(mStart - mCurPos, (uint64_t)sizeof(buf));
|
||||
nsresult rv = mInputStream->Read(buf, bufCount, &bytesRead);
|
||||
if (NS_SUCCEEDED(rv) && bytesRead == 0) {
|
||||
mClosed = true;
|
||||
return RunAsyncWaitCallback();
|
||||
}
|
||||
|
||||
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
|
||||
return mWeakAsyncInputStream->AsyncWait(this, 0, mStart - mCurPos,
|
||||
mAsyncWaitEventTarget);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || bytesRead == 0) {
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return RunAsyncWaitCallback();
|
||||
}
|
||||
|
||||
@ -427,6 +447,11 @@ SlicedInputStream::Seek(int32_t aWhence, int64_t aOffset)
|
||||
case NS_SEEK_END: {
|
||||
uint64_t available;
|
||||
rv = mInputStream->Available(&available);
|
||||
if (rv == NS_BASE_STREAM_CLOSED) {
|
||||
mClosed = true;
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -255,8 +255,6 @@ nsMultiplexInputStream::Close()
|
||||
}
|
||||
}
|
||||
|
||||
mAsyncWaitCallback = nullptr;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace testing {
|
||||
|
||||
@ -130,4 +131,88 @@ InputStreamCallback::OnInputStreamReady(nsIAsyncInputStream* aStream)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AsyncStringStream::AsyncStringStream(const nsACString& aBuffer)
|
||||
{
|
||||
NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStringStream::Available(uint64_t* aLength)
|
||||
{
|
||||
return mStream->Available(aLength);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStringStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
|
||||
{
|
||||
return mStream->Read(aBuffer, aCount, aReadCount);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStringStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
|
||||
uint32_t aCount, uint32_t *aResult)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStringStream::Close()
|
||||
{
|
||||
nsresult rv = mStream->Close();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
MaybeExecCallback(mCallback, mCallbackEventTarget);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStringStream::IsNonBlocking(bool* aNonBlocking)
|
||||
{
|
||||
return mStream->IsNonBlocking(aNonBlocking);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStringStream::CloseWithStatus(nsresult aStatus)
|
||||
{
|
||||
return Close();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStringStream::AsyncWait(nsIInputStreamCallback* aCallback,
|
||||
uint32_t aFlags, uint32_t aRequestedCount,
|
||||
nsIEventTarget* aEventTarget)
|
||||
{
|
||||
if (aFlags & nsIAsyncInputStream::WAIT_CLOSURE_ONLY) {
|
||||
mCallback = aCallback;
|
||||
mCallbackEventTarget = aEventTarget;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MaybeExecCallback(aCallback, aEventTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
AsyncStringStream::MaybeExecCallback(nsIInputStreamCallback* aCallback,
|
||||
nsIEventTarget* aEventTarget)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStreamCallback> callback = aCallback;
|
||||
nsCOMPtr<nsIAsyncInputStream> self = this;
|
||||
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
||||
"AsyncWait", [callback, self]() { callback->OnInputStreamReady(self); });
|
||||
|
||||
if (aEventTarget) {
|
||||
aEventTarget->Dispatch(r.forget());
|
||||
} else {
|
||||
r->Run();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(AsyncStringStream, nsIAsyncInputStream, nsIInputStream)
|
||||
|
||||
} // namespace testing
|
||||
|
@ -7,9 +7,11 @@
|
||||
#ifndef __Helpers_h
|
||||
#define __Helpers_h
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIAsyncOutputStream.h"
|
||||
#include "nsString.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
#include <stdint.h>
|
||||
|
||||
@ -68,6 +70,28 @@ public:
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
};
|
||||
|
||||
class AsyncStringStream final : public nsIAsyncInputStream
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> mStream;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
NS_DECL_NSIASYNCINPUTSTREAM
|
||||
|
||||
explicit AsyncStringStream(const nsACString& aBuffer);
|
||||
|
||||
private:
|
||||
~AsyncStringStream() = default;
|
||||
|
||||
void
|
||||
MaybeExecCallback(nsIInputStreamCallback* aCallback,
|
||||
nsIEventTarget* aEventTarget);
|
||||
|
||||
nsCOMPtr<nsIInputStreamCallback> mCallback;
|
||||
nsCOMPtr<nsIEventTarget> mCallbackEventTarget;
|
||||
};
|
||||
|
||||
} // namespace testing
|
||||
|
||||
#endif // __Helpers_h
|
||||
|
@ -5,12 +5,13 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIMultiplexInputStream.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "Helpers.h"
|
||||
|
||||
TEST(MultiplexInputStream, Seek_SET)
|
||||
{
|
||||
@ -101,3 +102,114 @@ TEST(MultiplexInputStream, Seek_SET)
|
||||
ASSERT_EQ((uint64_t)buf2.Length() - 6 + buf3.Length(), length);
|
||||
ASSERT_EQ(0, strncmp(readBuf, "The qu", count));
|
||||
}
|
||||
|
||||
already_AddRefed<nsIInputStream>
|
||||
CreateStreamHelper()
|
||||
{
|
||||
nsCOMPtr<nsIMultiplexInputStream> multiplexStream =
|
||||
do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
|
||||
|
||||
nsCString buf1;
|
||||
buf1.AssignLiteral("Hello");
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream1 = new testing::AsyncStringStream(buf1);
|
||||
multiplexStream->AppendStream(inputStream1);
|
||||
|
||||
nsCString buf2;
|
||||
buf2.AssignLiteral(" ");
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream2 = new testing::AsyncStringStream(buf2);
|
||||
multiplexStream->AppendStream(inputStream2);
|
||||
|
||||
nsCString buf3;
|
||||
buf3.AssignLiteral("World!");
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream3 = new testing::AsyncStringStream(buf3);
|
||||
multiplexStream->AppendStream(inputStream3);
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream(do_QueryInterface(multiplexStream));
|
||||
return stream.forget();
|
||||
}
|
||||
|
||||
// AsyncWait - without EventTarget
|
||||
TEST(TestMultiplexInputStream, AsyncWait_withoutEventTarget) {
|
||||
nsCOMPtr<nsIInputStream> is = CreateStreamHelper();
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is);
|
||||
ASSERT_TRUE(!!ais);
|
||||
|
||||
RefPtr<testing::InputStreamCallback> cb =
|
||||
new testing::InputStreamCallback();
|
||||
|
||||
ASSERT_EQ(NS_OK, ais->AsyncWait(cb, 0, 0, nullptr));
|
||||
ASSERT_FALSE(cb->Called());
|
||||
|
||||
// Eventually it is called.
|
||||
MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); }));
|
||||
ASSERT_TRUE(cb->Called());
|
||||
}
|
||||
|
||||
// AsyncWait - with EventTarget
|
||||
TEST(TestMultiplexInputStream, AsyncWait_withEventTarget) {
|
||||
nsCOMPtr<nsIInputStream> is = CreateStreamHelper();
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is);
|
||||
ASSERT_TRUE(!!ais);
|
||||
|
||||
RefPtr<testing::InputStreamCallback> cb =
|
||||
new testing::InputStreamCallback();
|
||||
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
||||
|
||||
ASSERT_EQ(NS_OK, ais->AsyncWait(cb, 0, 0, thread));
|
||||
|
||||
ASSERT_FALSE(cb->Called());
|
||||
|
||||
// Eventually it is called.
|
||||
MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); }));
|
||||
ASSERT_TRUE(cb->Called());
|
||||
}
|
||||
|
||||
// AsyncWait - without EventTarget - closureOnly
|
||||
TEST(TestMultiplexInputStream, AsyncWait_withoutEventTarget_closureOnly) {
|
||||
nsCOMPtr<nsIInputStream> is = CreateStreamHelper();
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is);
|
||||
ASSERT_TRUE(!!ais);
|
||||
|
||||
RefPtr<testing::InputStreamCallback> cb =
|
||||
new testing::InputStreamCallback();
|
||||
|
||||
ASSERT_EQ(NS_OK, ais->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY,
|
||||
0, nullptr));
|
||||
ASSERT_FALSE(cb->Called());
|
||||
|
||||
ais->CloseWithStatus(NS_ERROR_FAILURE);
|
||||
ASSERT_FALSE(cb->Called());
|
||||
|
||||
// Eventually it is called.
|
||||
MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); }));
|
||||
ASSERT_TRUE(cb->Called());
|
||||
}
|
||||
|
||||
// AsyncWait - withEventTarget - closureOnly
|
||||
TEST(TestMultiplexInputStream, AsyncWait_withEventTarget_closureOnly) {
|
||||
nsCOMPtr<nsIInputStream> is = CreateStreamHelper();
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is);
|
||||
ASSERT_TRUE(!!ais);
|
||||
|
||||
RefPtr<testing::InputStreamCallback> cb =
|
||||
new testing::InputStreamCallback();
|
||||
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
||||
|
||||
ASSERT_EQ(NS_OK, ais->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY,
|
||||
0, thread));
|
||||
|
||||
ASSERT_FALSE(cb->Called());
|
||||
ais->CloseWithStatus(NS_ERROR_FAILURE);
|
||||
ASSERT_FALSE(cb->Called());
|
||||
|
||||
// Eventually it is called.
|
||||
MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); }));
|
||||
ASSERT_TRUE(cb->Called());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user