Merge inbound to mozilla-central r=merge a=merge

This commit is contained in:
Coroiu Cristina 2017-11-07 01:04:34 +02:00
commit 282fd1a852
38 changed files with 528 additions and 1145 deletions

View File

@ -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',

View File

@ -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;
}

View File

@ -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__

View File

@ -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"

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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>&lt;A&gt;</TD></TR>
* <TR><TD>CONTEXT_IMAGE</TD><TD>&lt;IMG&gt;</TD></TR>
* <TR><TD>CONTEXT_IMAGE | CONTEXT_LINK</TD><TD>&lt;IMG&gt;
* with an &lt;A&gt; as an ancestor</TD></TR>
* <TR><TD>CONTEXT_INPUT</TD><TD>&lt;INPUT&gt;</TD></TR>
* <TR><TD>CONTEXT_TEXT</TD><TD>&lt;TEXTAREA&gt;</TD></TR>
* <TR><TD>CONTEXT_DOCUMENT</TD><TD>&lt;HTML&gt;</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);
};

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;

View File

@ -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());

View File

@ -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);

View 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");
`);
});

View File

@ -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);
}
} + ")()");

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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;
}

View File

@ -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());

View File

@ -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());
}
};
};

View File

@ -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);

View File

@ -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();

View File

@ -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());

View File

@ -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());
}
};
};

View File

@ -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")

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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())

View File

@ -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);

View File

@ -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;
});

View File

@ -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
}

View File

@ -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

View File

@ -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;
}

View File

@ -255,8 +255,6 @@ nsMultiplexInputStream::Close()
}
}
mAsyncWaitCallback = nullptr;
return rv;
}

View File

@ -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

View File

@ -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

View File

@ -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());
}