merge mozilla-central to autoland. r=merge a=merge

This commit is contained in:
Sebastian Hengst 2017-09-05 23:58:08 +02:00
commit 4d2231acf0
680 changed files with 114549 additions and 4823 deletions

View File

@ -437,9 +437,9 @@
position="bottomcenter topright"
tabspecific="true"
noautofocus="true"
copyURL-title="&copyURLCmd.label;"
copyURL-title="&copyLinkCmd.label;"
emailLink-title="&emailPageCmd.label;"
sendToDevice-title="&sendToDevice.label3;"
sendToDevice-title="&sendTabToDevice.label;"
sendToDevice-notReadyTitle="&sendToDevice.syncNotReady.label;">
<photonpanelmultiview id="pageActionPanelMultiView"
mainViewId="pageActionPanelMainView"

View File

@ -2271,7 +2271,8 @@
"audioPlaybackStopped", "pauseMedia", "stopMedia",
"resumeMedia", "mute", "unmute", "blockedPopups", "lastURI",
"purgeSessionHistory", "stopScroll", "startScroll",
"userTypedValue", "userTypedClear", "mediaBlocked"
"userTypedValue", "userTypedClear", "mediaBlocked",
"didStartLoadSinceLastUserTyping"
]</field>
<method name="_createLazyBrowser">
@ -2299,6 +2300,9 @@
return Services.io.newURI(url);
};
break;
case "didStartLoadSinceLastUserTyping":
getter = () => () => false;
break;
case "fullZoom":
case "textZoom":
getter = () => 1;
@ -2441,6 +2445,56 @@
</body>
</method>
<method name="discardBrowser">
<parameter name="aBrowser"/>
<body>
<![CDATA[
"use strict";
let tab = this.getTabForBrowser(aBrowser);
if (!tab ||
tab.selected ||
tab.closing ||
this._windowIsClosing ||
!aBrowser.isConnected ||
!aBrowser.isRemoteBrowser ||
aBrowser.frameLoader.tabParent.hasBeforeUnload) {
return;
}
// Set browser parameters for when browser is restored. Also remove
// listeners and set up lazy restore data in SessionStore. This must
// be done before aBrowser is destroyed and removed from the document.
tab._browserParams = { uriIsAboutBlank: aBrowser.currentURI.spec == "about:blank",
remoteType: aBrowser.remoteType,
usingPreloadedContent: false };
SessionStore.resetBrowserToLazyState(tab);
this._outerWindowIDBrowserMap.delete(aBrowser.outerWindowID);
// Remove the tab's filter and progress listener.
let filter = this._tabFilters.get(tab);
let listener = this._tabListeners.get(tab);
aBrowser.webProgress.removeProgressListener(filter);
filter.removeProgressListener(listener);
listener.destroy();
this._tabListeners.delete(tab);
this._tabFilters.delete(tab);
aBrowser.destroy();
let notificationbox = this.getNotificationBox(aBrowser);
this.mPanelContainer.removeChild(notificationbox);
tab.removeAttribute("linkedpanel");
this._createLazyBrowser(tab);
]]>
</body>
</method>
<method name="addTab">
<parameter name="aURI"/>
<parameter name="aReferrerURI"/>

View File

@ -370,6 +370,10 @@ this.SessionStore = {
return SessionStoreInternal.undoCloseById(aClosedId);
},
resetBrowserToLazyState(tab) {
return SessionStoreInternal.resetBrowserToLazyState(tab);
},
/**
* Determines whether the passed version number is compatible with
* the current version number of the SessionStore.
@ -1898,19 +1902,7 @@ var SessionStoreInternal = {
* bool Do not save state if we're updating an existing tab
*/
onTabRemove: function ssi_onTabRemove(aWindow, aTab, aNoNotification) {
let browser = aTab.linkedBrowser;
browser.removeEventListener("SwapDocShells", this);
browser.removeEventListener("oop-browser-crashed", this);
// If this tab was in the middle of restoring or still needs to be restored,
// we need to reset that state. If the tab was restoring, we will attempt to
// restore the next tab.
let previousState = browser.__SS_restoreState;
if (previousState) {
this._resetTabRestoringState(aTab);
if (previousState == TAB_STATE_RESTORING)
this.restoreNextTab();
}
this.cleanUpRemovedBrowser(aTab);
if (!aNoNotification) {
this.saveStateDelayed(aWindow);
@ -1979,6 +1971,57 @@ var SessionStoreInternal = {
this._closedTabs.set(permanentKey, {closedTabs, tabData});
},
/**
* Remove listeners which were added when browser was inserted and reset restoring state.
* Also re-instate lazy data and basically revert tab to its lazy browser state.
* @param aTab
* Tab reference
*/
resetBrowserToLazyState(aTab) {
let browser = aTab.linkedBrowser;
// Browser is already lazy so don't do anything.
if (!browser.isConnected) {
return;
}
this.cleanUpRemovedBrowser(aTab);
aTab.setAttribute("pending", "true");
this._lastKnownFrameLoader.delete(browser.permanentKey);
this._crashedBrowsers.delete(browser.permanentKey);
aTab.removeAttribute("crashed");
aTab.__SS_lazyData = {
url: browser.currentURI.spec,
title: aTab.label,
userTypedValue: browser.userTypedValue || "",
userTypedClear: browser.userTypedClear || 0
}
},
/**
* When a tab is removed or suspended, remove listeners and reset restoring state.
* @param aBrowser
* Browser reference
*/
cleanUpRemovedBrowser(aTab) {
let browser = aTab.linkedBrowser;
browser.removeEventListener("SwapDocShells", this);
browser.removeEventListener("oop-browser-crashed", this);
// If this tab was in the middle of restoring or still needs to be restored,
// we need to reset that state. If the tab was restoring, we will attempt to
// restore the next tab.
let previousState = browser.__SS_restoreState;
if (previousState) {
this._resetTabRestoringState(aTab);
if (previousState == TAB_STATE_RESTORING)
this.restoreNextTab();
}
},
/**
* Insert a given |tabData| object into the list of |closedTabs|. We will
* determine the right insertion point based on the .closedAt properties of

View File

@ -55,6 +55,7 @@ support-files =
restore_redirect_js.html
restore_redirect_target.html
browser_1234021_page.html
browser_1284886_suspend_tab.html
#NB: the following are disabled
# browser_464620_a.html
@ -228,6 +229,8 @@ skip-if = os == "mac" || (os == "linux" && debug) # linux, Bug 1348583
[browser_906076_lazy_tabs.js]
[browser_911547.js]
[browser_1284886_suspend_tab.js]
skip-if = !e10s
[browser_send_async_message_oom.js]
[browser_multiple_navigateAndRestore.js]
run-if = e10s

View File

@ -0,0 +1,12 @@
<html>
<head>
<script>
window.onbeforeunload = function() {
return true;
}
</script>
</head>
<body>
TEST PAGE
</body>
</html>

View File

@ -0,0 +1,49 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
add_task(async function test() {
let url = "about:robots";
let tab0 = gBrowser.tabs[0];
let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
gBrowser.discardBrowser(tab0.linkedBrowser);
ok(!tab0.linkedPanel, "tab0 is suspended");
await BrowserTestUtils.switchTab(gBrowser, tab0);
// Test that active tab is not able to be suspended.
gBrowser.discardBrowser(tab0.linkedBrowser);
ok(tab0.linkedPanel, "active tab is not able to be suspended");
// Test that tab that is closing is not able to be suspended.
gBrowser._beginRemoveTab(tab1);
gBrowser.discardBrowser(tab1.linkedBrowser);
ok(tab1.linkedPanel, "cannot suspend a tab that is closing");
gBrowser._endRemoveTab(tab1);
// Test that tab with beforeunload handler is not able to be suspended.
url = "http://example.com/browser/browser/components/sessionstore/test/browser_1284886_suspend_tab.html";
tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
await BrowserTestUtils.switchTab(gBrowser, tab0);
gBrowser.discardBrowser(tab1.linkedBrowser);
ok(tab1.linkedPanel, "cannot suspend a tab with beforeunload handler");
await promiseRemoveTab(tab1);
// Test that remote tab is not able to be suspended.
url = "about:robots";
tab1 = BrowserTestUtils.addTab(gBrowser, url, { forceNotRemote: true });
await promiseBrowserLoaded(tab1.linkedBrowser, true, url);
await BrowserTestUtils.switchTab(gBrowser, tab1);
await BrowserTestUtils.switchTab(gBrowser, tab0);
gBrowser.discardBrowser(tab1.linkedBrowser);
ok(tab1.linkedPanel, "cannot suspend a remote tab");
promiseRemoveTab(tab1);
});

View File

@ -553,7 +553,7 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY bookmarkThisLinkCmd.accesskey "L">
<!ENTITY bookmarkThisFrameCmd.label "Bookmark This Frame">
<!ENTITY bookmarkThisFrameCmd.accesskey "m">
<!ENTITY copyURLCmd.label "Copy URL">
<!ENTITY copyLinkCmd.label "Copy Link">
<!ENTITY copyURLFeedback.label "Copied!">
<!ENTITY emailPageCmd.label "Email Link…">
<!ENTITY emailPageCmd.accesskey "E">
@ -974,7 +974,7 @@ you can use these alternative items. Otherwise, their values should be empty. -
<!ENTITY pageAction.addToUrlbar.label "Add to Address Bar">
<!ENTITY pageAction.removeFromUrlbar.label "Remove from Address Bar">
<!ENTITY sendToDevice.label3 "Send Page to Device">
<!ENTITY sendTabToDevice.label "Send Tab to Device">
<!ENTITY sendToDevice.syncNotReady.label "Syncing Devices…">
<!ENTITY libraryButton.tooltip "View history, saved bookmarks, and more">

View File

@ -22,6 +22,7 @@ support-files =
doc_inspector_highlighter.html
doc_inspector_highlighter_rect.html
doc_inspector_highlighter_rect_iframe.html
doc_inspector_highlighter_scroll.html
doc_inspector_highlighter_xbl.xul
doc_inspector_infobar_01.html
doc_inspector_infobar_02.html
@ -70,6 +71,7 @@ skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keybo
[browser_inspector_highlighter-03.js]
[browser_inspector_highlighter-04.js]
[browser_inspector_highlighter-05.js]
[browser_inspector_highlighter-06.js]
[browser_inspector_highlighter-by-type.js]
[browser_inspector_highlighter-cancel.js]
[browser_inspector_highlighter-comments.js]

View File

@ -0,0 +1,31 @@
/* 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/. */
"use strict";
// Test that the browser remembers the previous scroll position after reload, even with
// Inspector opened - Bug 1382341.
// NOTE: Using a real file instead data: URL since the bug won't happen on data: URL
const TEST_URI = URL_ROOT + "doc_inspector_highlighter_scroll.html";
add_task(function* () {
let { inspector, testActor } = yield openInspectorForURL(TEST_URI);
yield testActor.scrollIntoView("a");
yield selectAndHighlightNode("a", inspector);
let markupLoaded = inspector.once("markuploaded");
let y = yield testActor.eval("window.pageYOffset");
isnot(y, 0, "window scrolled vertically.");
info("Reloading page.");
yield testActor.reload();
info("Waiting for markupview to load after reload.");
yield markupLoaded;
let newY = yield testActor.eval("window.pageYOffset");
is(y, newY, "window remember the previous scroll position.");
});

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
p {
height: 200vh;
}
</style>
</head>
<body>
<p>Bug 1382341 - test page reload and scroll position</p>
<a>An element anchor, used to scroll the page</a>
</body>
</html>

View File

@ -308,7 +308,6 @@ CanvasFrameAnonymousContentHelper.prototype = {
*/
_onWindowReady(e, {isTopLevel}) {
if (isTopLevel) {
this._remove();
this._removeAllListeners();
this.elements.clear();
this._insert();

View File

@ -2915,6 +2915,20 @@ nsDocShell::SetSecurityUI(nsISecureBrowserUI* aSecurityUI)
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetLoadURIDelegate(nsILoadURIDelegate** aLoadURIDelegate)
{
NS_IF_ADDREF(*aLoadURIDelegate = mLoadURIDelegate);
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::SetLoadURIDelegate(nsILoadURIDelegate* aLoadURIDelegate)
{
mLoadURIDelegate = aLoadURIDelegate;
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetUseErrorPages(bool* aUseErrorPages)
{
@ -9928,41 +9942,60 @@ nsDocShell::InternalLoad(nsIURI* aURI,
isTargetTopLevelDocShell = true;
}
if (contentType == nsIContentPolicy::TYPE_DOCUMENT &&
nsIOService::BlockToplevelDataUriNavigations()) {
bool isDataURI =
(NS_SUCCEEDED(aURI->SchemeIs("data", &isDataURI)) && isDataURI);
// Let's block all toplevel document navigations to a data: URI.
// In all cases where the toplevel document is navigated to a
// data: URI the triggeringPrincipal is a codeBasePrincipal, or
// a NullPrincipal. In other cases, e.g. typing a data: URL into
// the URL-Bar, the triggeringPrincipal is a SystemPrincipal;
// we don't want to block those loads. Only exception, loads coming
// from an external applicaton (e.g. Thunderbird) don't load
// using a codeBasePrincipal, but we want to block those loads.
bool loadFromExternal = (aLoadType == LOAD_NORMAL_EXTERNAL);
if (isDataURI && (loadFromExternal ||
!nsContentUtils::IsSystemPrincipal(aTriggeringPrincipal))) {
NS_ConvertUTF8toUTF16 specUTF16(aURI->GetSpecOrDefault());
if (specUTF16.Length() > 50) {
specUTF16.Truncate(50);
specUTF16.AppendLiteral("...");
}
const char16_t* params[] = { specUTF16.get() };
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("DATA_URI_BLOCKED"),
// no doc available, log to browser console
nullptr,
nsContentUtils::eSECURITY_PROPERTIES,
"BlockTopLevelDataURINavigation",
params, ArrayLength(params));
return NS_OK;
}
}
// If there's no targetDocShell, that means we are about to create a new
// window (or aWindowTarget is empty). Perform a content policy check before
// creating the window.
if (!targetDocShell) {
nsCOMPtr<Element> requestingElement;
// creating the window. Please note for all other docshell loads
// content policy checks are performed within the contentSecurityManager
// when the channel is about to be openend.
if (!targetDocShell && !aWindowTarget.IsEmpty()) {
MOZ_ASSERT(contentType == nsIContentPolicy::TYPE_DOCUMENT,
"opening a new window requires type to be TYPE_DOCUMENT");
nsISupports* requestingContext = nullptr;
if (contentType == nsIContentPolicy::TYPE_DOCUMENT) {
if (XRE_IsContentProcess()) {
// In e10s the child process doesn't have access to the element that
// contains the browsing context (because that element is in the chrome
// process). So we just pass mScriptGlobal.
requestingContext = ToSupports(mScriptGlobal);
} else {
// This is for loading non-e10s tabs and toplevel windows of various
// sorts.
// For the toplevel window cases, requestingElement will be null.
requestingElement = mScriptGlobal->AsOuter()->GetFrameElementInternal();
requestingContext = requestingElement;
}
if (XRE_IsContentProcess()) {
// In e10s the child process doesn't have access to the element that
// contains the browsing context (because that element is in the chrome
// process). So we just pass mScriptGlobal.
requestingContext = ToSupports(mScriptGlobal);
} else {
requestingElement = mScriptGlobal->AsOuter()->GetFrameElementInternal();
// This is for loading non-e10s tabs and toplevel windows of various
// sorts.
// For the toplevel window cases, requestingElement will be null.
nsCOMPtr<Element> requestingElement =
mScriptGlobal->AsOuter()->GetFrameElementInternal();
requestingContext = requestingElement;
#ifdef DEBUG
if (requestingElement) {
// Get the docshell type for requestingElement.
nsCOMPtr<nsIDocument> requestingDoc = requestingElement->OwnerDoc();
nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
// requestingElement docshell type = current docshell type.
MOZ_ASSERT(mItemType == elementDocShell->ItemType(),
"subframes should have the same docshell type as their parent");
}
#endif
}
// Since Content Policy checks are performed within docShell as well as
@ -10066,6 +10099,31 @@ nsDocShell::InternalLoad(nsIURI* aURI,
}
}
const nsIDocument* doc = mContentViewer ? mContentViewer->GetDocument()
: nullptr;
const bool isDocumentAuxSandboxed = doc &&
(doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
if (aURI && mLoadURIDelegate &&
(!targetDocShell || targetDocShell == static_cast<nsIDocShell*>(this))) {
// Dispatch only load requests for the current or a new window to the
// delegate, e.g., to allow for GeckoView apps to handle the load event
// outside of Gecko.
const int where = (aWindowTarget.IsEmpty() || targetDocShell)
? nsIBrowserDOMWindow::OPEN_CURRENTWINDOW
: nsIBrowserDOMWindow::OPEN_NEW;
if (where == nsIBrowserDOMWindow::OPEN_NEW && isDocumentAuxSandboxed) {
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
}
if (NS_SUCCEEDED(mLoadURIDelegate->LoadURI(aURI, where, aFlags,
aTriggeringPrincipal))) {
// The request has been handled, nothing to do here.
return NS_OK;
}
}
//
// Resolve the window target before going any further...
// If the load has been targeted to another DocShell, then transfer the
@ -10083,14 +10141,8 @@ nsDocShell::InternalLoad(nsIURI* aURI,
// if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
// (i.e. if allow-popups is specified)
NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
nsIDocument* doc = mContentViewer->GetDocument();
uint32_t sandboxFlags = 0;
if (doc) {
sandboxFlags = doc->GetSandboxFlags();
if (sandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) {
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
}
if (isDocumentAuxSandboxed) {
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
}
nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
@ -11029,17 +11081,40 @@ nsDocShell::DoURILoad(nsIURI* aURI,
nsCOMPtr<nsINode> loadingNode;
nsCOMPtr<nsPIDOMWindowOuter> loadingWindow;
nsCOMPtr<nsIPrincipal> loadingPrincipal;
nsCOMPtr<nsISupports> topLevelLoadingContext;
if (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
loadingNode = nullptr;
loadingPrincipal = nullptr;
loadingWindow = mScriptGlobal->AsOuter();
if (XRE_IsContentProcess()) {
// In e10s the child process doesn't have access to the element that
// contains the browsing context (because that element is in the chrome
// process).
nsCOMPtr<nsITabChild> tabChild = GetTabChild();
topLevelLoadingContext = ToSupports(tabChild);
} else {
// This is for loading non-e10s tabs and toplevel windows of various
// sorts.
// For the toplevel window cases, requestingElement will be null.
nsCOMPtr<Element> requestingElement =
loadingWindow->GetFrameElementInternal();
topLevelLoadingContext = requestingElement;
}
} else {
loadingWindow = nullptr;
loadingNode = mScriptGlobal->AsOuter()->GetFrameElementInternal();
if (loadingNode) {
// If we have a loading node, then use that as our loadingPrincipal.
loadingPrincipal = loadingNode->NodePrincipal();
#ifdef DEBUG
// Get the docshell type for requestingElement.
nsCOMPtr<nsIDocument> requestingDoc = loadingNode->OwnerDoc();
nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
// requestingElement docshell type = current docshell type.
MOZ_ASSERT(mItemType == elementDocShell->ItemType(),
"subframes should have the same docshell type as their parent");
#endif
} else {
// If this isn't a top-level load and mScriptGlobal's frame element is
// null, then the element got removed from the DOM while we were trying
@ -11100,42 +11175,11 @@ nsDocShell::DoURILoad(nsIURI* aURI,
nsCOMPtr<nsILoadInfo> loadInfo =
(aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) ?
new LoadInfo(loadingWindow, aTriggeringPrincipal,
new LoadInfo(loadingWindow, aTriggeringPrincipal, topLevelLoadingContext,
securityFlags) :
new LoadInfo(loadingPrincipal, aTriggeringPrincipal, loadingNode,
securityFlags, aContentPolicyType);
if (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT &&
nsIOService::BlockToplevelDataUriNavigations()) {
bool isDataURI =
(NS_SUCCEEDED(aURI->SchemeIs("data", &isDataURI)) && isDataURI);
// Let's block all toplevel document navigations to a data: URI.
// In all cases where the toplevel document is navigated to a
// data: URI the triggeringPrincipal is a codeBasePrincipal, or
// a NullPrincipal. In other cases, e.g. typing a data: URL into
// the URL-Bar, the triggeringPrincipal is a SystemPrincipal;
// we don't want to block those loads. Only exception, loads coming
// from an external applicaton (e.g. Thunderbird) don't load
// using a codeBasePrincipal, but we want to block those loads.
if (isDataURI && (aLoadFromExternal ||
!nsContentUtils::IsSystemPrincipal(aTriggeringPrincipal))) {
NS_ConvertUTF8toUTF16 specUTF16(aURI->GetSpecOrDefault());
if (specUTF16.Length() > 50) {
specUTF16.Truncate(50);
specUTF16.AppendLiteral("...");
}
const char16_t* params[] = { specUTF16.get() };
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("DATA_URI_BLOCKED"),
// no doc available, log to browser console
nullptr,
nsContentUtils::eSECURITY_PROPERTIES,
"BlockTopLevelDataURINavigation",
params, ArrayLength(params));
return NS_OK;
}
}
if (aPrincipalToInherit) {
loadInfo->SetPrincipalToInherit(aPrincipalToInherit);
}

View File

@ -61,6 +61,7 @@
#include "nsRect.h"
#include "Units.h"
#include "nsIDeprecationWarner.h"
#include "nsILoadURIDelegate.h"
namespace mozilla {
class Encoding;
@ -912,6 +913,8 @@ protected:
eCharsetReloadState mCharsetReloadState;
nsCOMPtr<nsILoadURIDelegate> mLoadURIDelegate;
// Offset in the parent's child list.
// -1 if the docshell is added dynamically to the parent shell.
int32_t mChildOffset;

View File

@ -53,6 +53,7 @@ interface nsITabParent;
interface nsITabChild;
interface nsICommandManager;
interface nsICommandParams;
interface nsILoadURIDelegate;
native TabChildRef(already_AddRefed<nsITabChild>);
[scriptable, builtinclass, uuid(049234fe-da10-478b-bc5d-bc6f9a1ba63d)]
@ -463,6 +464,13 @@ interface nsIDocShell : nsIDocShellTreeItem
*/
attribute nsISecureBrowserUI securityUI;
/**
* Object used to delegate URI loading to an upper context.
* Currently only set for GeckoView to allow handling of load requests
* at the application level.
*/
attribute nsILoadURIDelegate loadURIDelegate;
/**
* Cancel the XPCOM timers for each meta-refresh URI in this docshell,
* and this docshell's children, recursively. The meta-refresh timers can be

View File

@ -20,6 +20,7 @@
#include "nsIDOMElement.h"
#include "nsIDOMNode.h"
#include "nsIDOMWindow.h"
#include "nsITabChild.h"
#include "nsIContent.h"
#include "nsIImageLoadingContent.h"
#include "nsILoadContext.h"
@ -93,8 +94,9 @@ nsContentPolicy::CheckPolicy(CPMethod policyMethod,
{
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(requestingContext));
nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(requestingContext));
NS_ASSERTION(!requestingContext || node || window,
"Context should be a DOM node or a DOM window!");
nsCOMPtr<nsITabChild> tabChild(do_QueryInterface(requestingContext));
NS_ASSERTION(!requestingContext || node || window || tabChild,
"Context should be a DOM node, DOM window or a tabChild!");
}
#endif

View File

@ -175,7 +175,7 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
nsContentPolicyType internalContentPolicyType =
aLoadInfo->InternalContentPolicyType();
nsCString mimeTypeGuess;
nsCOMPtr<nsINode> requestingContext = nullptr;
nsCOMPtr<nsISupports> requestingContext = nullptr;
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
@ -229,7 +229,7 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
case nsIContentPolicy::TYPE_DOCUMENT: {
mimeTypeGuess = EmptyCString();
requestingContext = aLoadInfo->LoadingNode();
requestingContext = aLoadInfo->ContextForTopLevelLoad();
break;
}
@ -259,10 +259,13 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
case nsIContentPolicy::TYPE_XMLHTTPREQUEST: {
// alias nsIContentPolicy::TYPE_DATAREQUEST:
requestingContext = aLoadInfo->LoadingNode();
MOZ_ASSERT(!requestingContext ||
requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE,
"type_xml requires requestingContext of type Document");
#ifdef DEBUG
{
nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext);
MOZ_ASSERT(!node || node->NodeType() == nsIDOMNode::DOCUMENT_NODE,
"type_xml requires requestingContext of type Document");
}
#endif
// We're checking for the external TYPE_XMLHTTPREQUEST here in case
// an addon creates a request with that type.
if (internalContentPolicyType ==
@ -283,18 +286,26 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST: {
mimeTypeGuess = EmptyCString();
requestingContext = aLoadInfo->LoadingNode();
MOZ_ASSERT(!requestingContext ||
requestingContext->NodeType() == nsIDOMNode::ELEMENT_NODE,
"type_subrequest requires requestingContext of type Element");
#ifdef DEBUG
{
nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext);
MOZ_ASSERT(!node || node->NodeType() == nsIDOMNode::ELEMENT_NODE,
"type_subrequest requires requestingContext of type Element");
}
#endif
break;
}
case nsIContentPolicy::TYPE_DTD: {
mimeTypeGuess = EmptyCString();
requestingContext = aLoadInfo->LoadingNode();
MOZ_ASSERT(!requestingContext ||
requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE,
"type_dtd requires requestingContext of type Document");
#ifdef DEBUG
{
nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext);
MOZ_ASSERT(!node || node->NodeType() == nsIDOMNode::DOCUMENT_NODE,
"type_dtd requires requestingContext of type Document");
}
#endif
break;
}
@ -312,9 +323,13 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
mimeTypeGuess = EmptyCString();
}
requestingContext = aLoadInfo->LoadingNode();
MOZ_ASSERT(!requestingContext ||
requestingContext->NodeType() == nsIDOMNode::ELEMENT_NODE,
"type_media requires requestingContext of type Element");
#ifdef DEBUG
{
nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext);
MOZ_ASSERT(!node || node->NodeType() == nsIDOMNode::ELEMENT_NODE,
"type_media requires requestingContext of type Element");
}
#endif
break;
}
@ -342,18 +357,26 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
case nsIContentPolicy::TYPE_XSLT: {
mimeTypeGuess = NS_LITERAL_CSTRING("application/xml");
requestingContext = aLoadInfo->LoadingNode();
MOZ_ASSERT(!requestingContext ||
requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE,
"type_xslt requires requestingContext of type Document");
#ifdef DEBUG
{
nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext);
MOZ_ASSERT(!node || node->NodeType() == nsIDOMNode::DOCUMENT_NODE,
"type_xslt requires requestingContext of type Document");
}
#endif
break;
}
case nsIContentPolicy::TYPE_BEACON: {
mimeTypeGuess = EmptyCString();
requestingContext = aLoadInfo->LoadingNode();
MOZ_ASSERT(!requestingContext ||
requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE,
"type_beacon requires requestingContext of type Document");
#ifdef DEBUG
{
nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext);
MOZ_ASSERT(!node || node->NodeType() == nsIDOMNode::DOCUMENT_NODE,
"type_beacon requires requestingContext of type Document");
}
#endif
break;
}

View File

@ -691,49 +691,6 @@ HTMLEditor::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent)
return TypedText(str, eTypedText);
}
static void
AssertParserServiceIsCorrect(nsIAtom* aTag, bool aIsBlock)
{
#ifdef DEBUG
// Check this against what we would have said with the old code:
if (aTag == nsGkAtoms::p ||
aTag == nsGkAtoms::div ||
aTag == nsGkAtoms::blockquote ||
aTag == nsGkAtoms::h1 ||
aTag == nsGkAtoms::h2 ||
aTag == nsGkAtoms::h3 ||
aTag == nsGkAtoms::h4 ||
aTag == nsGkAtoms::h5 ||
aTag == nsGkAtoms::h6 ||
aTag == nsGkAtoms::ul ||
aTag == nsGkAtoms::ol ||
aTag == nsGkAtoms::dl ||
aTag == nsGkAtoms::noscript ||
aTag == nsGkAtoms::form ||
aTag == nsGkAtoms::hr ||
aTag == nsGkAtoms::table ||
aTag == nsGkAtoms::fieldset ||
aTag == nsGkAtoms::address ||
aTag == nsGkAtoms::col ||
aTag == nsGkAtoms::colgroup ||
aTag == nsGkAtoms::li ||
aTag == nsGkAtoms::dt ||
aTag == nsGkAtoms::dd ||
aTag == nsGkAtoms::legend) {
if (!aIsBlock) {
nsAutoString assertmsg (NS_LITERAL_STRING("Parser and editor disagree on blockness: "));
nsAutoString tagName;
aTag->ToString(tagName);
assertmsg.Append(tagName);
char* assertstr = ToNewCString(assertmsg);
NS_ASSERTION(aIsBlock, assertstr);
free(assertstr);
}
}
#endif // DEBUG
}
/**
* Returns true if the id represents an element of block type.
* Can be used to determine if a new paragraph should be started.
@ -753,10 +710,8 @@ HTMLEditor::NodeIsBlockStatic(const nsINode* aElement)
nsGkAtoms::tr,
nsGkAtoms::th,
nsGkAtoms::td,
nsGkAtoms::li,
nsGkAtoms::dt,
nsGkAtoms::dd,
nsGkAtoms::pre)) {
nsGkAtoms::dd)) {
return true;
}
@ -771,8 +726,6 @@ HTMLEditor::NodeIsBlockStatic(const nsINode* aElement)
isBlock);
MOZ_ASSERT(rv == NS_OK);
AssertParserServiceIsCorrect(aElement->NodeInfo()->NameAtom(), isBlock);
return isBlock;
}

View File

@ -435,18 +435,22 @@ GPUProcessManager::DisableWebRender(wr::WebRenderError aError)
if (!gfx::gfxVars::UseWebRender()) {
return;
}
// Disable WebRender
if (aError == wr::WebRenderError::INITIALIZE) {
// Disable WebRender
gfx::gfxConfig::GetFeature(gfx::Feature::WEBRENDER).ForceDisable(
gfx::FeatureStatus::Unavailable,
"WebRender initialization failed",
NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBRENDER_INITIALIZE"));
} else if (aError == wr::WebRenderError::MAKE_CURRENT) {
// Disable WebRender
gfx::gfxConfig::GetFeature(gfx::Feature::WEBRENDER).ForceDisable(
gfx::FeatureStatus::Unavailable,
"Failed to make render context current",
NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBRENDER_MAKE_CURRENT"));
} else if (aError == wr::WebRenderError::RENDER) {
gfx::gfxConfig::GetFeature(gfx::Feature::WEBRENDER).ForceDisable(
gfx::FeatureStatus::Unavailable,
"Failed to render WebRender",
NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBRENDER_RENDER"));
} else {
MOZ_ASSERT_UNREACHABLE("Invalid value");
}

View File

@ -302,18 +302,16 @@ RotatedContentBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds,
IntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
NS_ASSERTION(quadrantRect.Contains(bounds), "Messed up quadrants");
mLoanedTransform = mLoanedDrawTarget->GetTransform();
Matrix transform = Matrix(mLoanedTransform)
.PreTranslate(-quadrantRect.x,
-quadrantRect.y);
if (aSetTransform) {
mLoanedTransform = mLoanedDrawTarget->GetTransform();
Matrix transform = Matrix(mLoanedTransform)
.PreTranslate(-quadrantRect.x,
-quadrantRect.y);
mLoanedDrawTarget->SetTransform(transform);
mSetTransform = true;
} else {
MOZ_ASSERT(aOutMatrix);
*aOutMatrix = transform;
*aOutMatrix = Matrix::Translation(-quadrantRect.x, -quadrantRect.y);
mSetTransform = false;
}

View File

@ -139,11 +139,18 @@ bool SkAnalyticEdge::setLine(const SkPoint& p0, const SkPoint& p1) {
// We must set X/Y using the same way (e.g., times 4, to FDot6, then to Fixed) as Quads/Cubics.
// Otherwise the order of the edge might be wrong due to precision limit.
const int accuracy = kDefaultAccuracy;
#ifdef SK_RASTERIZE_EVEN_ROUNDING
SkFixed x0 = SkFDot6ToFixed(SkScalarRoundToFDot6(p0.fX, accuracy)) >> accuracy;
SkFixed y0 = SnapY(SkFDot6ToFixed(SkScalarRoundToFDot6(p0.fY, accuracy)) >> accuracy);
SkFixed x1 = SkFDot6ToFixed(SkScalarRoundToFDot6(p1.fX, accuracy)) >> accuracy;
SkFixed y1 = SnapY(SkFDot6ToFixed(SkScalarRoundToFDot6(p1.fY, accuracy)) >> accuracy);
#else
const int multiplier = (1 << kDefaultAccuracy);
SkFixed x0 = SkFDot6ToFixed(SkScalarToFDot6(p0.fX * multiplier)) >> accuracy;
SkFixed y0 = SnapY(SkFDot6ToFixed(SkScalarToFDot6(p0.fY * multiplier)) >> accuracy);
SkFixed x1 = SkFDot6ToFixed(SkScalarToFDot6(p1.fX * multiplier)) >> accuracy;
SkFixed y1 = SnapY(SkFDot6ToFixed(SkScalarToFDot6(p1.fY * multiplier)) >> accuracy);
#endif
int winding = 1;

View File

@ -501,6 +501,7 @@ private:
DECL_GFX_PREF(Live, "gl.require-hardware", RequireHardwareGL, bool, false);
DECL_GFX_PREF(Live, "gl.use-tls-is-current", UseTLSIsCurrent, int32_t, 0);
DECL_GFX_PREF(Live, "image.cache.factor2.threshold-surfaces", ImageCacheFactor2ThresholdSurfaces, int32_t, -1);
DECL_GFX_PREF(Once, "image.cache.size", ImageCacheSize, int32_t, 5*1024*1024);
DECL_GFX_PREF(Once, "image.cache.timeweight", ImageCacheTimeWeight, int32_t, 500);
DECL_GFX_PREF(Live, "image.decode-immediately.enabled", ImageDecodeImmediatelyEnabled, bool, false);

View File

@ -2,8 +2,6 @@
* 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/. */
extern crate app_units;
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;

View File

@ -201,7 +201,7 @@ pub fn main_wrapper(example: &mut Example,
}
renderer.update();
renderer.render(DeviceUintSize::new(width, height));
renderer.render(DeviceUintSize::new(width, height)).unwrap();
window.swap_buffers().ok();
}

View File

@ -2,8 +2,6 @@
* 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/. */
extern crate app_units;
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;

View File

@ -17,6 +17,9 @@
uniform sampler2DArray sCacheA8;
uniform sampler2DArray sCacheRGBA8;
// An A8 target for standalone tasks that is available to all passes.
uniform sampler2DArray sSharedCacheA8;
uniform sampler2D sGradients;
struct RectWithSize {
@ -767,7 +770,7 @@ BoxShadow fetch_boxshadow_direct(ivec2 address) {
}
void write_clip(vec2 global_pos, ClipArea area) {
vec2 texture_size = vec2(textureSize(sCacheA8, 0).xy);
vec2 texture_size = vec2(textureSize(sSharedCacheA8, 0).xy);
vec2 uv = global_pos + area.task_bounds.xy - area.screen_origin_target_index.xy;
vClipMaskUvBounds = area.task_bounds / texture_size.xyxy;
vClipMaskUv = vec3(uv / texture_size, area.screen_origin_target_index.z);
@ -806,7 +809,7 @@ float do_clip() {
vec4(vClipMaskUv.xy, vClipMaskUvBounds.zw));
// check for the dummy bounds, which are given to the opaque objects
return vClipMaskUvBounds.xy == vClipMaskUvBounds.zw ? 1.0:
all(inside) ? textureLod(sCacheA8, vClipMaskUv, 0.0).r : 0.0;
all(inside) ? textureLod(sSharedCacheA8, vClipMaskUv, 0.0).r : 0.0;
}
#ifdef WR_FEATURE_DITHERING

View File

@ -18,6 +18,6 @@ void main(void) {
uv = mix(vCacheUvRectCoords.xy, vCacheUvRectCoords.zw, uv);
// Modulate the box shadow by the color.
float mask = texture(sColor1, vec3(uv, vUv.z)).r;
float mask = texture(sSharedCacheA8, vec3(uv, vUv.z)).r;
oFragColor = clip_scale * dither(vColor * vec4(1.0, 1.0, 1.0, mask));
}

View File

@ -27,7 +27,7 @@ void main(void) {
vUv.xy = (vi.local_pos - prim.local_rect.p0) / patch_size;
vMirrorPoint = 0.5 * prim.local_rect.size / patch_size;
vec2 texture_size = vec2(textureSize(sCacheA8, 0));
vec2 texture_size = vec2(textureSize(sSharedCacheA8, 0));
vCacheUvRectCoords = vec4(patch_origin, patch_origin + patch_size_device_pixels) / texture_size.xyxy;
vColor = bs.color;

View File

@ -251,7 +251,7 @@ impl ClipScrollTree {
// TODO(gw): This is an ugly borrow check workaround to clone these.
// Restructure this to avoid the clones!
let (state, node_children) = {
let mut node = match self.nodes.get_mut(&layer_id) {
let node = match self.nodes.get_mut(&layer_id) {
Some(node) => node,
None => return,
};

View File

@ -409,7 +409,7 @@ impl FontContext {
let current_height = (i * metrics.rasterized_width * 4) as usize;
let end_row = current_height + (metrics.rasterized_width as usize * 4);
for mut pixel in rasterized_pixels[current_height .. end_row].chunks_mut(4) {
for pixel in rasterized_pixels[current_height .. end_row].chunks_mut(4) {
pixel[0] = 255 - pixel[0];
pixel[1] = 255 - pixel[1];
pixel[2] = 255 - pixel[2];

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{FontInstance, FontInstancePlatformOptions, FontKey, FontRenderMode};
use api::{GlyphDimensions, GlyphKey, GlyphOptions, SubpixelDirection};
use api::{GlyphDimensions, GlyphKey};
use gamma_lut::{GammaLut, Color as ColorLut};
use internal_types::FastHashMap;

View File

@ -176,7 +176,7 @@ impl RenderBackend {
}
fn process_document(&mut self, document_id: DocumentId, message: DocumentMsg,
frame_counter: u32, mut profile_counters: &mut BackendProfileCounters)
frame_counter: u32, profile_counters: &mut BackendProfileCounters)
-> DocumentOp
{
let doc = self.documents.get_mut(&document_id).expect("No document?");

View File

@ -68,6 +68,16 @@ impl RenderTaskTree {
}
}
// If this task can be shared between multiple
// passes, render it in the first pass so that
// it is available to all subsequent passes.
let pass_index = if task.is_shared() {
debug_assert!(task.children.is_empty());
0
} else {
pass_index
};
let pass = &mut passes[pass_index];
pass.add_render_task(id);
}
@ -514,6 +524,29 @@ impl RenderTask {
}
}
// Check if this task wants to be made available as an input
// to all passes (except the first) in the render task tree.
// To qualify for this, the task needs to have no children / dependencies.
// Currently, this is only supported for A8 targets, but it can be
// trivially extended to also support RGBA8 targets in the future
// if we decide that is useful.
pub fn is_shared(&self) -> bool {
match self.kind {
RenderTaskKind::Alpha(..) |
RenderTaskKind::CachePrimitive(..) |
RenderTaskKind::VerticalBlur(..) |
RenderTaskKind::Readback(..) |
RenderTaskKind::HorizontalBlur(..) => false,
RenderTaskKind::CacheMask(..) |
RenderTaskKind::BoxShadow(..) => true,
RenderTaskKind::Alias(..) => {
panic!("BUG: is_shared() called on aliased task");
}
}
}
pub fn set_alias(&mut self, id: RenderTaskId) {
debug_assert!(self.cache_key.is_some());
// TODO(gw): We can easily handle invalidation of tasks that

View File

@ -141,6 +141,10 @@ enum TextureSampler {
Layers,
RenderTasks,
Dither,
// A special sampler that is bound to the A8 output of
// the *first* pass. Items rendered in this target are
// available as inputs to tasks in any subsequent pass.
SharedCacheA8,
}
impl TextureSampler {
@ -168,6 +172,7 @@ impl Into<TextureSlot> for TextureSampler {
TextureSampler::Layers => TextureSlot(6),
TextureSampler::RenderTasks => TextureSlot(7),
TextureSampler::Dither => TextureSlot(8),
TextureSampler::SharedCacheA8 => TextureSlot(9),
}
}
}
@ -394,14 +399,31 @@ impl SourceTextureResolver {
}
}
fn set_cache_textures(&mut self,
a8_texture: Option<Texture>,
rgba8_texture: Option<Texture>) {
// todo(gw): make the texture recycling cleaner...
debug_assert!(self.cache_a8_texture.is_none());
debug_assert!(self.cache_rgba8_texture.is_none());
self.cache_a8_texture = a8_texture;
self.cache_rgba8_texture = rgba8_texture;
fn end_pass(&mut self,
pass_index: usize,
pass_count: usize,
mut a8_texture: Option<Texture>,
mut rgba8_texture: Option<Texture>,
a8_pool: &mut Vec<Texture>,
rgba8_pool: &mut Vec<Texture>) {
// If we have cache textures from previous pass, return them to the pool.
rgba8_pool.extend(self.cache_rgba8_texture.take());
a8_pool.extend(self.cache_a8_texture.take());
if pass_index == pass_count-1 {
// On the last pass, return the textures from this pass to the pool.
if let Some(texture) = rgba8_texture.take() {
rgba8_pool.push(texture);
}
if let Some(texture) = a8_texture.take() {
a8_pool.push(texture);
}
} else {
// We have another pass to process, make these textures available
// as inputs to the next pass.
self.cache_rgba8_texture = rgba8_texture.take();
self.cache_a8_texture = a8_texture.take();
}
}
// Bind a source texture to the device.
@ -716,9 +738,14 @@ impl LazilyCompiledShader {
Ok(shader)
}
fn bind(&mut self, device: &mut Device, projection: &Transform3D<f32>) {
let program = self.get(device)
.expect("Unable to get shader!");
fn bind(&mut self, device: &mut Device, projection: &Transform3D<f32>, renderer_errors: &mut Vec<RendererError>) {
let program = match self.get(device) {
Ok(program) => program,
Err(e) => {
renderer_errors.push(RendererError::from(e));
return;
}
};
device.bind_program(program);
device.set_uniforms(program, projection);
}
@ -808,10 +835,11 @@ impl PrimitiveShader {
fn bind(&mut self,
device: &mut Device,
transform_kind: TransformedRectKind,
projection: &Transform3D<f32>) {
projection: &Transform3D<f32>,
renderer_errors: &mut Vec<RendererError>) {
match transform_kind {
TransformedRectKind::AxisAligned => self.simple.bind(device, projection),
TransformedRectKind::Complex => self.transform.bind(device, projection),
TransformedRectKind::AxisAligned => self.simple.bind(device, projection, renderer_errors),
TransformedRectKind::Complex => self.transform.bind(device, projection, renderer_errors),
}
}
@ -854,6 +882,7 @@ fn create_prim_shader(name: &'static str,
("sLayers", TextureSampler::Layers),
("sRenderTasks", TextureSampler::RenderTasks),
("sResourceCache", TextureSampler::ResourceCache),
("sSharedCacheA8", TextureSampler::SharedCacheA8),
]);
}
@ -875,6 +904,7 @@ fn create_clip_shader(name: &'static str, device: &mut Device) -> Result<Program
("sLayers", TextureSampler::Layers),
("sRenderTasks", TextureSampler::RenderTasks),
("sResourceCache", TextureSampler::ResourceCache),
("sSharedCacheA8", TextureSampler::SharedCacheA8),
]);
}
@ -983,6 +1013,8 @@ pub struct Renderer {
/// application to provide external buffers for image data.
external_image_handler: Option<Box<ExternalImageHandler>>,
renderer_errors: Vec<RendererError>,
/// List of profile results from previous frames. Can be retrieved
/// via get_frame_profiles().
cpu_profiles: VecDeque<CpuProfile>,
@ -990,18 +1022,18 @@ pub struct Renderer {
}
#[derive(Debug)]
pub enum InitError {
pub enum RendererError {
Shader(ShaderError),
Thread(std::io::Error),
MaxTextureSize,
}
impl From<ShaderError> for InitError {
fn from(err: ShaderError) -> Self { InitError::Shader(err) }
impl From<ShaderError> for RendererError {
fn from(err: ShaderError) -> Self { RendererError::Shader(err) }
}
impl From<std::io::Error> for InitError {
fn from(err: std::io::Error) -> Self { InitError::Thread(err) }
impl From<std::io::Error> for RendererError {
fn from(err: std::io::Error) -> Self { RendererError::Thread(err) }
}
impl Renderer {
@ -1022,7 +1054,7 @@ impl Renderer {
/// let (renderer, sender) = Renderer::new(opts);
/// ```
/// [rendereroptions]: struct.RendererOptions.html
pub fn new(gl: Rc<gl::Gl>, mut options: RendererOptions) -> Result<(Renderer, RenderApiSender), InitError> {
pub fn new(gl: Rc<gl::Gl>, mut options: RendererOptions) -> Result<(Renderer, RenderApiSender), RendererError> {
let (api_tx, api_rx) = try!{ channel::msg_channel() };
let (payload_tx, payload_rx) = try!{ channel::payload_channel() };
@ -1050,7 +1082,7 @@ impl Renderer {
let min_texture_size = 512;
if device_max_size < min_texture_size {
println!("Device reporting insufficient max texture size ({})", device_max_size);
return Err(InitError::MaxTextureSize);
return Err(RendererError::MaxTextureSize);
}
let max_device_size = cmp::max(
cmp::min(device_max_size, options.max_texture_size.unwrap_or(device_max_size)),
@ -1502,6 +1534,7 @@ impl Renderer {
gpu_cache_texture,
texture_cache_upload_pbo,
texture_resolver,
renderer_errors: Vec::new(),
};
let sender = RenderApiSender::new(api_tx, payload_tx);
@ -1711,7 +1744,7 @@ impl Renderer {
///
/// A Frame is supplied by calling [`generate_frame()`][genframe].
/// [genframe]: ../../webrender_api/struct.DocumentApi.html#method.generate_frame
pub fn render(&mut self, framebuffer_size: DeviceUintSize) {
pub fn render(&mut self, framebuffer_size: DeviceUintSize) -> Result<(), Vec<RendererError>> {
profile_scope!("render");
if let Some(mut frame) = self.current_frame.take() {
@ -1806,6 +1839,11 @@ impl Renderer {
// Restore frame - avoid borrow checker!
self.current_frame = Some(frame);
}
if !self.renderer_errors.is_empty() {
let errors = mem::replace(&mut self.renderer_errors, Vec::new());
return Err(errors);
}
Ok(())
}
pub fn layers_are_bouncing_back(&self) -> bool {
@ -1953,42 +1991,42 @@ impl Renderer {
let marker = match key.kind {
AlphaBatchKind::Composite { .. } => {
self.ps_composite.bind(&mut self.device, projection);
self.ps_composite.bind(&mut self.device, projection, &mut self.renderer_errors);
GPU_TAG_PRIM_COMPOSITE
}
AlphaBatchKind::HardwareComposite => {
self.ps_hw_composite.bind(&mut self.device, projection);
self.ps_hw_composite.bind(&mut self.device, projection, &mut self.renderer_errors);
GPU_TAG_PRIM_HW_COMPOSITE
}
AlphaBatchKind::SplitComposite => {
self.ps_split_composite.bind(&mut self.device, projection);
self.ps_split_composite.bind(&mut self.device, projection, &mut self.renderer_errors);
GPU_TAG_PRIM_SPLIT_COMPOSITE
}
AlphaBatchKind::Blend => {
self.ps_blend.bind(&mut self.device, projection);
self.ps_blend.bind(&mut self.device, projection, &mut self.renderer_errors);
GPU_TAG_PRIM_BLEND
}
AlphaBatchKind::Rectangle => {
if needs_clipping {
self.ps_rectangle_clip.bind(&mut self.device, transform_kind, projection);
self.ps_rectangle_clip.bind(&mut self.device, transform_kind, projection, &mut self.renderer_errors);
} else {
self.ps_rectangle.bind(&mut self.device, transform_kind, projection);
self.ps_rectangle.bind(&mut self.device, transform_kind, projection, &mut self.renderer_errors);
}
GPU_TAG_PRIM_RECT
}
AlphaBatchKind::Line => {
self.ps_line.bind(&mut self.device, transform_kind, projection);
self.ps_line.bind(&mut self.device, transform_kind, projection, &mut self.renderer_errors);
GPU_TAG_PRIM_LINE
}
AlphaBatchKind::TextRun => {
match key.blend_mode {
BlendMode::Subpixel(..) => {
self.ps_text_run_subpixel.bind(&mut self.device, transform_kind, projection);
self.ps_text_run_subpixel.bind(&mut self.device, transform_kind, projection, &mut self.renderer_errors);
}
BlendMode::Alpha |
BlendMode::PremultipliedAlpha |
BlendMode::None => {
self.ps_text_run.bind(&mut self.device, transform_kind, projection);
self.ps_text_run.bind(&mut self.device, transform_kind, projection, &mut self.renderer_errors);
}
};
GPU_TAG_PRIM_TEXT_RUN
@ -1997,7 +2035,7 @@ impl Renderer {
self.ps_image[image_buffer_kind as usize]
.as_mut()
.expect("Unsupported image shader kind")
.bind(&mut self.device, transform_kind, projection);
.bind(&mut self.device, transform_kind, projection, &mut self.renderer_errors);
GPU_TAG_PRIM_IMAGE
}
AlphaBatchKind::YuvImage(image_buffer_kind, format, color_space) => {
@ -2007,35 +2045,35 @@ impl Renderer {
self.ps_yuv_image[shader_index]
.as_mut()
.expect("Unsupported YUV shader kind")
.bind(&mut self.device, transform_kind, projection);
.bind(&mut self.device, transform_kind, projection, &mut self.renderer_errors);
GPU_TAG_PRIM_YUV_IMAGE
}
AlphaBatchKind::BorderCorner => {
self.ps_border_corner.bind(&mut self.device, transform_kind, projection);
self.ps_border_corner.bind(&mut self.device, transform_kind, projection, &mut self.renderer_errors);
GPU_TAG_PRIM_BORDER_CORNER
}
AlphaBatchKind::BorderEdge => {
self.ps_border_edge.bind(&mut self.device, transform_kind, projection);
self.ps_border_edge.bind(&mut self.device, transform_kind, projection, &mut self.renderer_errors);
GPU_TAG_PRIM_BORDER_EDGE
}
AlphaBatchKind::AlignedGradient => {
self.ps_gradient.bind(&mut self.device, transform_kind, projection);
self.ps_gradient.bind(&mut self.device, transform_kind, projection, &mut self.renderer_errors);
GPU_TAG_PRIM_GRADIENT
}
AlphaBatchKind::AngleGradient => {
self.ps_angle_gradient.bind(&mut self.device, transform_kind, projection);
self.ps_angle_gradient.bind(&mut self.device, transform_kind, projection, &mut self.renderer_errors);
GPU_TAG_PRIM_ANGLE_GRADIENT
}
AlphaBatchKind::RadialGradient => {
self.ps_radial_gradient.bind(&mut self.device, transform_kind, projection);
self.ps_radial_gradient.bind(&mut self.device, transform_kind, projection, &mut self.renderer_errors);
GPU_TAG_PRIM_RADIAL_GRADIENT
}
AlphaBatchKind::BoxShadow => {
self.ps_box_shadow.bind(&mut self.device, transform_kind, projection);
self.ps_box_shadow.bind(&mut self.device, transform_kind, projection, &mut self.renderer_errors);
GPU_TAG_PRIM_BOX_SHADOW
}
AlphaBatchKind::CacheImage => {
self.ps_cache_image.bind(&mut self.device, transform_kind, projection);
self.ps_cache_image.bind(&mut self.device, transform_kind, projection, &mut self.renderer_errors);
GPU_TAG_PRIM_CACHE_IMAGE
}
};
@ -2151,7 +2189,7 @@ impl Renderer {
let _gm = self.gpu_profile.add_marker(GPU_TAG_BLUR);
self.device.set_blend(false);
self.cs_blur.bind(&mut self.device, projection);
self.cs_blur.bind(&mut self.device, projection, &mut self.renderer_errors);
if !target.vertical_blurs.is_empty() {
self.draw_instanced_batch(&target.vertical_blurs,
@ -2177,7 +2215,7 @@ impl Renderer {
self.device.set_blend_mode_alpha();
let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_TEXT_RUN);
self.cs_text_run.bind(&mut self.device, projection);
self.cs_text_run.bind(&mut self.device, projection, &mut self.renderer_errors);
self.draw_instanced_batch(&target.text_run_cache_prims,
VertexArrayKind::Primitive,
&target.text_run_textures);
@ -2189,7 +2227,7 @@ impl Renderer {
self.device.set_blend_mode_alpha();
let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_LINE);
self.cs_line.bind(&mut self.device, projection);
self.cs_line.bind(&mut self.device, projection, &mut self.renderer_errors);
self.draw_instanced_batch(&target.line_cache_prims,
VertexArrayKind::Primitive,
&BatchTextures::no_texture());
@ -2293,7 +2331,7 @@ impl Renderer {
if !target.box_shadow_cache_prims.is_empty() {
self.device.set_blend(false);
let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_BOX_SHADOW);
self.cs_box_shadow.bind(&mut self.device, projection);
self.cs_box_shadow.bind(&mut self.device, projection, &mut self.renderer_errors);
self.draw_instanced_batch(&target.box_shadow_cache_prims,
VertexArrayKind::CacheBoxShadow,
&BatchTextures::no_texture());
@ -2309,7 +2347,7 @@ impl Renderer {
if !target.clip_batcher.border_clears.is_empty() {
let _gm2 = GpuMarker::new(self.device.rc_gl(), "clip borders [clear]");
self.device.set_blend(false);
self.cs_clip_border.bind(&mut self.device, projection);
self.cs_clip_border.bind(&mut self.device, projection, &mut self.renderer_errors);
self.draw_instanced_batch(&target.clip_batcher.border_clears,
VertexArrayKind::Clip,
&BatchTextures::no_texture());
@ -2324,7 +2362,7 @@ impl Renderer {
// a max blend mode here is fine.
self.device.set_blend(true);
self.device.set_blend_mode_max();
self.cs_clip_border.bind(&mut self.device, projection);
self.cs_clip_border.bind(&mut self.device, projection, &mut self.renderer_errors);
self.draw_instanced_batch(&target.clip_batcher.borders,
VertexArrayKind::Clip,
&BatchTextures::no_texture());
@ -2337,7 +2375,7 @@ impl Renderer {
// draw rounded cornered rectangles
if !target.clip_batcher.rectangles.is_empty() {
let _gm2 = GpuMarker::new(self.device.rc_gl(), "clip rectangles");
self.cs_clip_rectangle.bind(&mut self.device, projection);
self.cs_clip_rectangle.bind(&mut self.device, projection, &mut self.renderer_errors);
self.draw_instanced_batch(&target.clip_batcher.rectangles,
VertexArrayKind::Clip,
&BatchTextures::no_texture());
@ -2352,7 +2390,7 @@ impl Renderer {
SourceTexture::Invalid,
]
};
self.cs_clip_image.bind(&mut self.device, projection);
self.cs_clip_image.bind(&mut self.device, projection, &mut self.renderer_errors);
self.draw_instanced_batch(items,
VertexArrayKind::Clip,
&textures);
@ -2487,7 +2525,8 @@ impl Renderer {
self.device.bind_texture(TextureSampler::Layers, &self.layer_texture.texture);
self.device.bind_texture(TextureSampler::RenderTasks, &self.render_task_texture.texture);
self.texture_resolver.set_cache_textures(None, None);
debug_assert!(self.texture_resolver.cache_a8_texture.is_none());
debug_assert!(self.texture_resolver.cache_rgba8_texture.is_none());
}
fn draw_tile_frame(&mut self,
@ -2509,8 +2548,9 @@ impl Renderer {
self.device.clear_target(Some(self.clear_color.to_array()), Some(1.0));
} else {
self.start_frame(frame);
let pass_count = frame.passes.len();
for pass in &mut frame.passes {
for (pass_index, pass) in frame.passes.iter_mut().enumerate() {
let size;
let clear_color;
let projection;
@ -2564,25 +2604,20 @@ impl Renderer {
}
// Return the texture IDs to the pool for next frame.
if let Some(texture) = self.texture_resolver.cache_rgba8_texture.take() {
self.color_render_targets.push(texture);
self.texture_resolver.end_pass(pass_index,
pass_count,
pass.alpha_texture.take(),
pass.color_texture.take(),
&mut self.alpha_render_targets,
&mut self.color_render_targets);
// After completing the first pass, make the A8 target available as an
// input to any subsequent passes.
if pass_index == 0 {
if let Some(shared_alpha_texture) = self.texture_resolver.resolve(&SourceTexture::CacheA8) {
self.device.bind_texture(TextureSampler::SharedCacheA8, shared_alpha_texture);
}
}
if let Some(texture) = self.texture_resolver.cache_a8_texture.take() {
self.alpha_render_targets.push(texture);
}
self.texture_resolver.set_cache_textures(pass.alpha_texture.take(),
pass.color_texture.take());
}
// Return the texture IDs to the pool for next frame.
if let Some(texture) = self.texture_resolver.cache_rgba8_texture.take() {
self.color_render_targets.push(texture);
}
if let Some(texture) = self.texture_resolver.cache_a8_texture.take() {
self.alpha_render_targets.push(texture);
}
self.color_render_targets.reverse();

View File

@ -626,7 +626,7 @@ impl DisplayListBuilder {
font_key: FontInstanceKey,
color: ColorF,
glyphs: I) {
let mut font_glyphs = self.glyphs.entry((font_key, color))
let font_glyphs = self.glyphs.entry((font_key, color))
.or_insert(FastHashSet::default());
font_glyphs.extend(glyphs);

View File

@ -174,7 +174,9 @@ RendererOGL::Render()
mSyncObject->Synchronize();
}
wr_renderer_render(mRenderer, size.width, size.height);
if (!wr_renderer_render(mRenderer, size.width, size.height)) {
NotifyWebRenderError(WebRenderError::RENDER);
}
mGL->SwapBuffers();
mWidget->PostRender(&widgetContext);

View File

@ -703,6 +703,7 @@ typedef Variant<layers::FrameMetrics::ViewID, WrClipId> ScrollOrClipId;
enum class WebRenderError : int8_t {
INITIALIZE = 0,
MAKE_CURRENT,
RENDER,
Sentinel /* this must be last for serialization purposes. */
};

View File

@ -443,8 +443,20 @@ pub extern "C" fn wr_renderer_update(renderer: &mut Renderer) {
#[no_mangle]
pub extern "C" fn wr_renderer_render(renderer: &mut Renderer,
width: u32,
height: u32) {
renderer.render(DeviceUintSize::new(width, height));
height: u32) -> bool {
match renderer.render(DeviceUintSize::new(width, height)) {
Ok(()) => true,
Err(errors) => {
for e in errors {
println!(" Failed to render: {:?}", e);
let msg = CString::new(format!("wr_renderer_render: {:?}", e)).unwrap();
unsafe {
gfx_critical_note(msg.as_ptr());
}
}
false
},
}
}
// Call wr_renderer_render() before calling this function.

View File

@ -1145,7 +1145,7 @@ void wr_renderer_readback(Renderer *aRenderer,
WR_FUNC;
WR_INLINE
void wr_renderer_render(Renderer *aRenderer,
bool wr_renderer_render(Renderer *aRenderer,
uint32_t aWidth,
uint32_t aHeight)
WR_FUNC;

View File

@ -200,6 +200,12 @@ DecodedSurfaceProvider::FinishDecoding()
// Send notifications.
NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
// If we have a new and complete surface, we can try to prune similarly sized
// surfaces if the cache supports it.
if (mSurface && mSurface->IsFinished()) {
SurfaceCache::PruneImage(ImageKey(mImage));
}
// Destroy our decoder; we don't need it anymore. (And if we don't destroy it,
// our surface can never be optimized, because the decoder has a
// RawAccessFrameRef to it.)

View File

@ -145,6 +145,9 @@ DecoderFactory::CreateDecoder(DecoderType aType,
WrapNotNull(new DecodedSurfaceProvider(aImage,
surfaceKey,
WrapNotNull(decoder)));
if (aDecoderFlags & DecoderFlags::CANNOT_SUBSTITUTE) {
provider->Availability().SetCannotSubstitute();
}
// Attempt to insert the surface provider into the surface cache right away so
// we won't trigger any more decoders with the same parameters.

View File

@ -23,7 +23,15 @@ enum class DecoderFlags : uint8_t
FIRST_FRAME_ONLY = 1 << 0,
IS_REDECODE = 1 << 1,
IMAGE_IS_TRANSIENT = 1 << 2,
ASYNC_NOTIFY = 1 << 3
ASYNC_NOTIFY = 1 << 3,
/**
* By default, a surface is considered substitutable. That means callers are
* willing to accept a less than ideal match to display. If a caller requires
* a specific size and won't accept alternatives, then this flag should be
* set.
*/
CANNOT_SUBSTITUTE = 1 << 4
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DecoderFlags)

View File

@ -134,6 +134,12 @@ DynamicImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const
return NS_ERROR_NOT_IMPLEMENTED;
}
size_t
DynamicImage::GetNativeSizesLength() const
{
return 0;
}
NS_IMETHODIMP
DynamicImage::GetIntrinsicSize(nsSize* aSize)
{

View File

@ -32,6 +32,7 @@ public:
// Inherited methods from Image.
nsresult GetNativeSizes(nsTArray<gfx::IntSize>& aNativeSizes) const override;
size_t GetNativeSizesLength() const override;
virtual already_AddRefed<ProgressTracker> GetProgressTracker() override;
virtual size_t SizeOfSourceWithComputedFallback(
SizeOfState& aState) const override;

View File

@ -530,7 +530,9 @@ DoCollectSizeOfCompositingSurfaces(const RawAccessFrameRef& aSurface,
PlaybackType::eStatic);
// Create a counter for this surface.
SurfaceMemoryCounter counter(key, /* aIsLocked = */ true, aType);
SurfaceMemoryCounter counter(key, /* aIsLocked = */ true,
/* aCannotSubstitute */ false,
/* aIsFactor2 */ false, aType);
// Extract the surface's memory usage information.
size_t heap = 0, nonHeap = 0, handles = 0;

View File

@ -72,11 +72,15 @@ struct SurfaceMemoryCounter
{
SurfaceMemoryCounter(const SurfaceKey& aKey,
bool aIsLocked,
bool aCannotSubstitute,
bool aIsFactor2,
SurfaceMemoryCounterType aType =
SurfaceMemoryCounterType::NORMAL)
: mKey(aKey)
, mType(aType)
, mIsLocked(aIsLocked)
, mCannotSubstitute(aCannotSubstitute)
, mIsFactor2(aIsFactor2)
{ }
const SurfaceKey& Key() const { return mKey; }
@ -84,12 +88,16 @@ struct SurfaceMemoryCounter
const MemoryCounter& Values() const { return mValues; }
SurfaceMemoryCounterType Type() const { return mType; }
bool IsLocked() const { return mIsLocked; }
bool CannotSubstitute() const { return mCannotSubstitute; }
bool IsFactor2() const { return mIsFactor2; }
private:
const SurfaceKey mKey;
MemoryCounter mValues;
const SurfaceMemoryCounterType mType;
const bool mIsLocked;
const bool mCannotSubstitute;
const bool mIsFactor2;
};
struct ImageMemoryCounter

View File

@ -145,6 +145,12 @@ ImageWrapper::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const
return mInnerImage->GetNativeSizes(aNativeSizes);
}
size_t
ImageWrapper::GetNativeSizesLength() const
{
return mInnerImage->GetNativeSizesLength();
}
NS_IMETHODIMP
ImageWrapper::GetIntrinsicSize(nsSize* aSize)
{

View File

@ -23,6 +23,7 @@ public:
// Inherited methods from Image.
nsresult GetNativeSizes(nsTArray<gfx::IntSize>& aNativeSizes) const override;
size_t GetNativeSizesLength() const override;
virtual already_AddRefed<ProgressTracker> GetProgressTracker() override;
virtual size_t

View File

@ -12,6 +12,7 @@
#define mozilla_image_LookupResult_h
#include "mozilla/Attributes.h"
#include "mozilla/gfx/Point.h" // for IntSize
#include "mozilla/Move.h"
#include "ISurfaceProvider.h"
@ -24,8 +25,15 @@ enum class MatchType : uint8_t
PENDING, // Found a matching placeholder, but no surface.
EXACT, // Found a surface that matches exactly.
SUBSTITUTE_BECAUSE_NOT_FOUND, // No exact match, but found a similar one.
SUBSTITUTE_BECAUSE_PENDING // Found a similar surface and a placeholder
SUBSTITUTE_BECAUSE_PENDING, // Found a similar surface and a placeholder
// for an exact match.
/* No exact match, but this should be considered an exact match for purposes
* of deciding whether or not to request a new decode. This is because the
* cache has determined that callers require too many size variants of this
* image. It determines the set of sizes which best represent the image, and
* will only suggest decoding of unavailable sizes from that set. */
SUBSTITUTE_BECAUSE_BEST
};
/**
@ -60,16 +68,31 @@ public:
"NOT_FOUND or PENDING do not make sense with a surface");
}
LookupResult(DrawableSurface&& aSurface, MatchType aMatchType,
const gfx::IntSize& aSuggestedSize)
: mSurface(Move(aSurface))
, mMatchType(aMatchType)
, mSuggestedSize(aSuggestedSize)
{
MOZ_ASSERT(!mSuggestedSize.IsEmpty());
MOZ_ASSERT(!mSurface || aMatchType == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND,
"Only SUBSTITUTE_BECAUSE_NOT_FOUND make sense with no surface");
MOZ_ASSERT(mSurface || aMatchType == MatchType::NOT_FOUND,
"NOT_FOUND does not make sense with a surface");
}
LookupResult& operator=(LookupResult&& aOther)
{
MOZ_ASSERT(&aOther != this, "Self-move-assignment is not supported");
mSurface = Move(aOther.mSurface);
mMatchType = aOther.mMatchType;
mSuggestedSize = aOther.mSuggestedSize;
return *this;
}
DrawableSurface& Surface() { return mSurface; }
const DrawableSurface& Surface() const { return mSurface; }
const gfx::IntSize& SuggestedSize() const { return mSuggestedSize; }
/// @return true if this LookupResult contains a surface.
explicit operator bool() const { return bool(mSurface); }
@ -82,6 +105,10 @@ private:
DrawableSurface mSurface;
MatchType mMatchType;
/// If given, the size the caller should request a decode at. This may or may
/// not match the size the caller requested from the cache.
gfx::IntSize mSuggestedSize;
};
} // namespace image

View File

@ -241,6 +241,21 @@ RasterImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const
return NS_OK;
}
//******************************************************************************
size_t
RasterImage::GetNativeSizesLength() const
{
if (mError || !mHasSize) {
return 0;
}
if (mNativeSizes.IsEmpty()) {
return 1;
}
return mNativeSizes.Length();
}
//******************************************************************************
NS_IMETHODIMP
RasterImage::GetIntrinsicSize(nsSize* aSize)
@ -351,6 +366,14 @@ RasterImage::LookupFrame(const IntSize& aSize,
!mAnimationState || mAnimationState->KnownFrameCount() < 1,
"Animated frames should be locked");
// The surface cache may suggest the preferred size we are supposed to
// decode at. This should only happen if we accept substitutions.
if (!result.SuggestedSize().IsEmpty()) {
MOZ_ASSERT(!(aFlags & FLAG_SYNC_DECODE) &&
(aFlags & FLAG_HIGH_QUALITY_SCALING));
requestedSize = result.SuggestedSize();
}
bool ranSync = Decode(requestedSize, aFlags, aPlaybackType);
// If we can or did sync decode, we should already have the frame.
@ -1263,6 +1286,12 @@ RasterImage::Decode(const IntSize& aSize,
if (mHasBeenDecoded) {
decoderFlags |= DecoderFlags::IS_REDECODE;
}
if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
// Used SurfaceCache::Lookup instead of SurfaceCache::LookupBestMatch. That
// means the caller can handle a differently sized surface to be returned
// at any point.
decoderFlags |= DecoderFlags::CANNOT_SUBSTITUTE;
}
SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
if (IsOpaque()) {

View File

@ -161,6 +161,7 @@ public:
#endif
nsresult GetNativeSizes(nsTArray<gfx::IntSize>& aNativeSizes) const override;
size_t GetNativeSizesLength() const override;
virtual nsresult StartAnimation() override;
virtual nsresult StopAnimation() override;

View File

@ -161,6 +161,9 @@ public:
return !IsPlaceholder() && mIsLocked && mProvider->IsLocked();
}
void SetCannotSubstitute() { mProvider->Availability().SetCannotSubstitute(); }
bool CannotSubstitute() const { return mProvider->Availability().CannotSubstitute(); }
bool IsPlaceholder() const { return mProvider->Availability().IsPlaceholder(); }
bool IsDecoded() const { return !IsPlaceholder() && mProvider->IsFinished(); }
@ -182,10 +185,12 @@ public:
, mMallocSizeOf(aMallocSizeOf)
{ }
void Add(NotNull<CachedSurface*> aCachedSurface)
void Add(NotNull<CachedSurface*> aCachedSurface, bool aIsFactor2)
{
SurfaceMemoryCounter counter(aCachedSurface->GetSurfaceKey(),
aCachedSurface->IsLocked());
aCachedSurface->IsLocked(),
aCachedSurface->CannotSubstitute(),
aIsFactor2);
if (aCachedSurface->IsPlaceholder()) {
return;
@ -231,12 +236,26 @@ AreaOfIntSize(const IntSize& aSize) {
*
* ImageSurfaceCache also keeps track of whether its associated image is locked
* or unlocked.
*
* The cache may also enter "factor of 2" mode which occurs when the number of
* surfaces in the cache exceeds the "image.cache.factor2.threshold-surfaces"
* pref plus the number of native sizes of the image. When in "factor of 2"
* mode, the cache will strongly favour sizes which are a factor of 2 of the
* largest native size. It accomplishes this by suggesting a factor of 2 size
* when lookups fail and substituting the nearest factor of 2 surface to the
* ideal size as the "best" available (as opposed to subsitution but not found).
* This allows us to minimize memory consumption and CPU time spent decoding
* when a website requires many variants of the same surface.
*/
class ImageSurfaceCache
{
~ImageSurfaceCache() { }
public:
ImageSurfaceCache() : mLocked(false) { }
ImageSurfaceCache()
: mLocked(false)
, mFactor2Mode(false)
, mFactor2Pruned(false)
{ }
MOZ_DECLARE_REFCOUNTED_TYPENAME(ImageSurfaceCache)
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageSurfaceCache)
@ -263,21 +282,63 @@ public:
return surface.forget();
}
already_AddRefed<CachedSurface> Lookup(const SurfaceKey& aSurfaceKey)
already_AddRefed<CachedSurface> Lookup(const SurfaceKey& aSurfaceKey,
bool aForAccess)
{
RefPtr<CachedSurface> surface;
mSurfaces.Get(aSurfaceKey, getter_AddRefs(surface));
if (aForAccess) {
if (surface) {
// We don't want to allow factor of 2 mode pruning to release surfaces
// for which the callers will accept no substitute.
surface->SetCannotSubstitute();
} else if (!mFactor2Mode) {
// If no exact match is found, and this is for use rather than internal
// accounting (i.e. insert and removal), we know this will trigger a
// decode. Make sure we switch now to factor of 2 mode if necessary.
MaybeSetFactor2Mode();
}
}
return surface.forget();
}
Pair<already_AddRefed<CachedSurface>, MatchType>
/**
* @returns A tuple containing the best matching CachedSurface if available,
* a MatchType describing how the CachedSurface was selected, and
* an IntSize which is the size the caller should choose to decode
* at should it attempt to do so.
*/
Tuple<already_AddRefed<CachedSurface>, MatchType, IntSize>
LookupBestMatch(const SurfaceKey& aIdealKey)
{
// Try for an exact match first.
RefPtr<CachedSurface> exactMatch;
mSurfaces.Get(aIdealKey, getter_AddRefs(exactMatch));
if (exactMatch && exactMatch->IsDecoded()) {
return MakePair(exactMatch.forget(), MatchType::EXACT);
if (exactMatch) {
if (exactMatch->IsDecoded()) {
return MakeTuple(exactMatch.forget(), MatchType::EXACT, IntSize());
}
} else if (!mFactor2Mode) {
// If no exact match is found, and we are not in factor of 2 mode, then
// we know that we will trigger a decode because at best we will provide
// a substitute. Make sure we switch now to factor of 2 mode if necessary.
MaybeSetFactor2Mode();
}
// Try for a best match second, if using compact.
IntSize suggestedSize = SuggestedSize(aIdealKey.Size());
if (mFactor2Mode) {
if (!exactMatch) {
SurfaceKey compactKey = aIdealKey.CloneWithSize(suggestedSize);
mSurfaces.Get(compactKey, getter_AddRefs(exactMatch));
if (exactMatch && exactMatch->IsDecoded()) {
return MakeTuple(exactMatch.forget(),
MatchType::SUBSTITUTE_BECAUSE_BEST,
suggestedSize);
}
}
}
// There's no perfect match, so find the best match we can.
@ -319,40 +380,35 @@ public:
}
SurfaceKey bestMatchKey = bestMatch->GetSurfaceKey();
// Compare sizes. We use an area-based heuristic here instead of computing a
// truly optimal answer, since it seems very unlikely to make a difference
// for realistic sizes.
int64_t idealArea = AreaOfIntSize(aIdealKey.Size());
int64_t currentArea = AreaOfIntSize(currentKey.Size());
int64_t bestMatchArea = AreaOfIntSize(bestMatchKey.Size());
// If the best match is smaller than the ideal size, prefer bigger sizes.
if (bestMatchArea < idealArea) {
if (currentArea > bestMatchArea) {
bestMatch = current;
}
continue;
}
// Other, prefer sizes closer to the ideal size, but still not smaller.
if (idealArea <= currentArea && currentArea < bestMatchArea) {
if (CompareArea(aIdealKey.Size(), bestMatchKey.Size(),
currentKey.Size())) {
bestMatch = current;
continue;
}
// This surface isn't an improvement over the current best match.
}
MatchType matchType;
if (bestMatch) {
if (!exactMatch) {
// No exact match, but we found a substitute.
matchType = MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND;
const IntSize& bestMatchSize = bestMatch->GetSurfaceKey().Size();
if (mFactor2Mode && suggestedSize == bestMatchSize) {
// No exact match, but this is the best we are willing to decode.
matchType = MatchType::SUBSTITUTE_BECAUSE_BEST;
} else {
// No exact match, but we found a substitute.
matchType = MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND;
}
} else if (exactMatch != bestMatch) {
// The exact match is still decoding, but we found a substitute.
matchType = MatchType::SUBSTITUTE_BECAUSE_PENDING;
} else {
// The exact match is still decoding, but it's the best we've got.
matchType = MatchType::EXACT;
const IntSize& bestMatchSize = bestMatch->GetSurfaceKey().Size();
if (mFactor2Mode && aIdealKey.Size() != bestMatchSize) {
// The best factor of 2 match is still decoding.
matchType = MatchType::SUBSTITUTE_BECAUSE_BEST;
} else {
// The exact match is still decoding, but it's the best we've got.
matchType = MatchType::EXACT;
}
}
} else {
if (exactMatch) {
@ -365,7 +421,198 @@ public:
}
}
return MakePair(bestMatch.forget(), matchType);
return MakeTuple(bestMatch.forget(), matchType, suggestedSize);
}
void MaybeSetFactor2Mode()
{
MOZ_ASSERT(!mFactor2Mode);
// Typically an image cache will not have too many size-varying surfaces, so
// if we exceed the given threshold, we should consider using a subset.
int32_t thresholdSurfaces = gfxPrefs::ImageCacheFactor2ThresholdSurfaces();
if (thresholdSurfaces < 0 ||
mSurfaces.Count() <= static_cast<uint32_t>(thresholdSurfaces)) {
return;
}
// Determine how many native surfaces this image has. Zero means we either
// don't know yet (in which case do nothing), or we don't want to limit the
// number of surfaces for this image.
//
// XXX(aosmond): Vector images have zero native sizes. This is because they
// are regenerated at the given size. There isn't an equivalent concept to
// the native size (and w/h ratio) to provide a frame of reference to what
// are "good" sizes. While it is desirable to have a similar mechanism as
// that for raster images, it will need a different approach.
auto first = ConstIter();
NotNull<CachedSurface*> current = WrapNotNull(first.UserData());
Image* image = static_cast<Image*>(current->GetImageKey());
size_t nativeSizes = image->GetNativeSizesLength();
if (nativeSizes == 0) {
return;
}
// Increase the threshold by the number of native sizes. This ensures that
// we do not prevent decoding of the image at all its native sizes. It does
// not guarantee we will provide a surface at that size however (i.e. many
// other sized surfaces are requested, in addition to the native sizes).
thresholdSurfaces += nativeSizes;
if (mSurfaces.Count() <= static_cast<uint32_t>(thresholdSurfaces)) {
return;
}
// Get our native size. While we know the image should be fully decoded,
// if it is an SVG, it is valid to have a zero size. We can't do compacting
// in that case because we need to know the width/height ratio to define a
// candidate set.
IntSize nativeSize;
if (NS_FAILED(image->GetWidth(&nativeSize.width)) ||
NS_FAILED(image->GetHeight(&nativeSize.height)) ||
nativeSize.IsEmpty()) {
return;
}
// We have a valid size, we can change modes.
mFactor2Mode = true;
}
template<typename Function>
void Prune(Function&& aRemoveCallback)
{
if (!mFactor2Mode || mFactor2Pruned) {
return;
}
// Attempt to discard any surfaces which are not factor of 2 and the best
// factor of 2 match exists.
bool hasNotFactorSize = false;
for (auto iter = mSurfaces.Iter(); !iter.Done(); iter.Next()) {
NotNull<CachedSurface*> current = WrapNotNull(iter.UserData());
const SurfaceKey& currentKey = current->GetSurfaceKey();
const IntSize& currentSize = currentKey.Size();
// First we check if someone requested this size and would not accept
// an alternatively sized surface.
if (current->CannotSubstitute()) {
continue;
}
// Next we find the best factor of 2 size for this surface. If this
// surface is a factor of 2 size, then we want to keep it.
IntSize bestSize = SuggestedSize(currentSize);
if (bestSize == currentSize) {
continue;
}
// Check the cache for a surface with the same parameters except for the
// size which uses the closest factor of 2 size.
SurfaceKey compactKey = currentKey.CloneWithSize(bestSize);
RefPtr<CachedSurface> compactMatch;
mSurfaces.Get(compactKey, getter_AddRefs(compactMatch));
if (compactMatch && compactMatch->IsDecoded()) {
aRemoveCallback(current);
iter.Remove();
} else {
hasNotFactorSize = true;
}
}
// We have no surfaces that are not factor of 2 sized, so we can stop
// pruning henceforth, because we avoid the insertion of new surfaces that
// don't match our sizing set (unless the caller won't accept a
// substitution.)
if (!hasNotFactorSize) {
mFactor2Pruned = true;
}
}
IntSize SuggestedSize(const IntSize& aSize) const
{
// When not in factor of 2 mode, we can always decode at the given size.
if (!mFactor2Mode) {
return aSize;
}
MOZ_ASSERT(!IsEmpty());
// This bit of awkwardness gets the largest native size of the image.
auto iter = ConstIter();
NotNull<CachedSurface*> firstSurface = WrapNotNull(iter.UserData());
Image* image = static_cast<Image*>(firstSurface->GetImageKey());
IntSize factorSize;
if (NS_FAILED(image->GetWidth(&factorSize.width)) ||
NS_FAILED(image->GetHeight(&factorSize.height)) ||
factorSize.IsEmpty()) {
// We should not have entered factor of 2 mode without a valid size, and
// several successfully decoded surfaces.
MOZ_ASSERT_UNREACHABLE("Expected valid native size!");
return aSize;
}
// Start with the native size as the best first guess.
IntSize bestSize = factorSize;
factorSize.width /= 2;
factorSize.height /= 2;
while (!factorSize.IsEmpty()) {
if (!CompareArea(aSize, bestSize, factorSize)) {
// This size is not better than the last. Since we proceed from largest
// to smallest, we know that the next size will not be better if the
// previous size was rejected. Break early.
break;
}
// The current factor of 2 size is better than the last selected size.
bestSize = factorSize;
factorSize.width /= 2;
factorSize.height /= 2;
}
return bestSize;
}
bool CompareArea(const IntSize& aIdealSize,
const IntSize& aBestSize,
const IntSize& aSize) const
{
// Compare sizes. We use an area-based heuristic here instead of computing a
// truly optimal answer, since it seems very unlikely to make a difference
// for realistic sizes.
int64_t idealArea = AreaOfIntSize(aIdealSize);
int64_t currentArea = AreaOfIntSize(aSize);
int64_t bestMatchArea = AreaOfIntSize(aBestSize);
// If the best match is smaller than the ideal size, prefer bigger sizes.
if (bestMatchArea < idealArea) {
if (currentArea > bestMatchArea) {
return true;
}
return false;
}
// Other, prefer sizes closer to the ideal size, but still not smaller.
if (idealArea <= currentArea && currentArea < bestMatchArea) {
return true;
}
// This surface isn't an improvement over the current best match.
return false;
}
void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
MallocSizeOf aMallocSizeOf)
{
CachedSurface::SurfaceMemoryReport report(aCounters, aMallocSizeOf);
for (auto iter = ConstIter(); !iter.Done(); iter.Next()) {
NotNull<CachedSurface*> surface = WrapNotNull(iter.UserData());
const IntSize& size = surface->GetSurfaceKey().Size();
bool factor2Size = false;
if (mFactor2Mode) {
factor2Size = (size == SuggestedSize(size));
}
report.Add(surface, factor2Size);
}
}
SurfaceTable::Iterator ConstIter() const
@ -377,8 +624,16 @@ public:
bool IsLocked() const { return mLocked; }
private:
SurfaceTable mSurfaces;
bool mLocked;
SurfaceTable mSurfaces;
bool mLocked;
// True in "factor of 2" mode.
bool mFactor2Mode;
// True if all non-factor of 2 surfaces have been removed from the cache. Note
// that this excludes unsubstitutable sizes.
bool mFactor2Pruned;
};
/**
@ -503,7 +758,7 @@ public:
return InsertOutcome::SUCCESS;
}
void Remove(NotNull<CachedSurface*> aSurface,
bool Remove(NotNull<CachedSurface*> aSurface,
const StaticMutexAutoLock& aAutoLock)
{
ImageKey imageKey = aSurface->GetImageKey();
@ -527,7 +782,10 @@ public:
// have been removed, so it is safe to free while holding the lock.
if (cache->IsEmpty() && !cache->IsLocked()) {
mImageCaches.Remove(imageKey);
return true;
}
return false;
}
void StartTracking(NotNull<CachedSurface*> aSurface,
@ -591,7 +849,7 @@ public:
return LookupResult(MatchType::NOT_FOUND);
}
RefPtr<CachedSurface> surface = cache->Lookup(aSurfaceKey);
RefPtr<CachedSurface> surface = cache->Lookup(aSurfaceKey, aMarkUsed);
if (!surface) {
// Lookup in the per-image cache missed.
return LookupResult(MatchType::NOT_FOUND);
@ -637,8 +895,10 @@ public:
RefPtr<CachedSurface> surface;
DrawableSurface drawableSurface;
MatchType matchType = MatchType::NOT_FOUND;
IntSize suggestedSize;
while (true) {
Tie(surface, matchType) = cache->LookupBestMatch(aSurfaceKey);
Tie(surface, matchType, suggestedSize)
= cache->LookupBestMatch(aSurfaceKey);
if (!surface) {
return LookupResult(matchType); // Lookup in the per-image cache missed.
@ -651,7 +911,9 @@ public:
// The surface was released by the operating system. Remove the cache
// entry as well.
Remove(WrapNotNull(surface), aAutoLock);
if (Remove(WrapNotNull(surface), aAutoLock)) {
break;
}
}
MOZ_ASSERT_IF(matchType == MatchType::EXACT,
@ -662,10 +924,19 @@ public:
surface->GetSurfaceKey().Playback() == aSurfaceKey.Playback() &&
surface->GetSurfaceKey().Flags() == aSurfaceKey.Flags());
if (matchType == MatchType::EXACT) {
if (matchType == MatchType::EXACT ||
matchType == MatchType::SUBSTITUTE_BECAUSE_BEST) {
MarkUsed(WrapNotNull(surface), WrapNotNull(cache), aAutoLock);
}
// When the caller may choose to decode at an uncached size because there is
// no pending decode at the requested size, we should give it the alternative
// size it should decode at.
if (matchType == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND ||
matchType == MatchType::NOT_FOUND) {
return LookupResult(Move(drawableSurface), matchType, suggestedSize);
}
return LookupResult(Move(drawableSurface), matchType);
}
@ -760,6 +1031,18 @@ public:
return cache.forget();
}
void PruneImage(const ImageKey aImageKey, const StaticMutexAutoLock& aAutoLock)
{
RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
if (!cache) {
return; // No cached surfaces for this image, so nothing to do.
}
cache->Prune([this, &aAutoLock](NotNull<CachedSurface*> aSurface) -> void {
StopTracking(aSurface, aAutoLock);
});
}
void DiscardAll(const StaticMutexAutoLock& aAutoLock)
{
// Remove in order of cost because mCosts is an array and the other data
@ -856,10 +1139,7 @@ public:
}
// Report all surfaces in the per-image cache.
CachedSurface::SurfaceMemoryReport report(aCounters, aMallocSizeOf);
for (auto iter = cache->ConstIter(); !iter.Done(); iter.Next()) {
report.Add(WrapNotNull(iter.UserData()));
}
cache->CollectSizeOfSurfaces(aCounters, aMallocSizeOf);
}
private:
@ -918,7 +1198,8 @@ private:
return; // No cached surfaces for this image.
}
RefPtr<CachedSurface> surface = cache->Lookup(aSurfaceKey);
RefPtr<CachedSurface> surface =
cache->Lookup(aSurfaceKey, /* aForAccess = */ false);
if (!surface) {
return; // Lookup in the per-image cache missed.
}
@ -1208,6 +1489,15 @@ SurfaceCache::RemoveImage(const ImageKey aImageKey)
}
}
/* static */ void
SurfaceCache::PruneImage(const ImageKey aImageKey)
{
StaticMutexAutoLock lock(sInstanceMutex);
if (sInstance) {
sInstance->PruneImage(aImageKey, lock);
}
}
/* static */ void
SurfaceCache::DiscardAll()
{

View File

@ -68,6 +68,11 @@ public:
return hash;
}
SurfaceKey CloneWithSize(const IntSize& aSize) const
{
return SurfaceKey(aSize, mSVGContext, mPlayback, mFlags);
}
const IntSize& Size() const { return mSize; }
Maybe<SVGImageContext> SVGContext() const { return mSVGContext; }
PlaybackType Playback() const { return mPlayback; }
@ -129,6 +134,10 @@ VectorSurfaceKey(const gfx::IntSize& aSize,
* changes), an ISurfaceProvider which starts as a placeholder can only reveal
* the fact that it now has a surface available via a call to
* SurfaceCache::SurfaceAvailable().
*
* It also tracks whether or not there are "explicit" users of this surface
* which will not accept substitutes. This is used by SurfaceCache when pruning
* unnecessary surfaces from the cache.
*/
class AvailabilityState
{
@ -138,15 +147,22 @@ public:
bool IsAvailable() const { return mIsAvailable; }
bool IsPlaceholder() const { return !mIsAvailable; }
bool CannotSubstitute() const { return mCannotSubstitute; }
void SetCannotSubstitute() { mCannotSubstitute = true; }
private:
friend class SurfaceCacheImpl;
explicit AvailabilityState(bool aIsAvailable) : mIsAvailable(aIsAvailable) { }
explicit AvailabilityState(bool aIsAvailable)
: mIsAvailable(aIsAvailable)
, mCannotSubstitute(false)
{ }
void SetAvailable() { mIsAvailable = true; }
bool mIsAvailable;
bool mIsAvailable : 1;
bool mCannotSubstitute : 1;
};
enum class InsertOutcome : uint8_t {
@ -388,6 +404,16 @@ struct SurfaceCache
*/
static void RemoveImage(const ImageKey aImageKey);
/**
* Attempts to remove cache entries (including placeholders) associated with
* the given image from the cache, assuming there is an equivalent entry that
* it is able substitute that entry with. Note that this only applies if the
* image is in factor of 2 mode. If it is not, this operation does nothing.
*
* @param aImageKey The image whose cache which should be pruned.
*/
static void PruneImage(const ImageKey aImageKey);
/**
* Evicts all evictable entries from the cache.
*

View File

@ -528,6 +528,13 @@ VectorImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const
return NS_ERROR_NOT_IMPLEMENTED;
}
//******************************************************************************
size_t
VectorImage::GetNativeSizesLength() const
{
return 0;
}
//******************************************************************************
NS_IMETHODIMP_(void)
VectorImage::RequestRefresh(const TimeStamp& aTime)

View File

@ -35,6 +35,7 @@ public:
// Methods inherited from Image
nsresult GetNativeSizes(nsTArray<gfx::IntSize>& aNativeSizes) const override;
size_t GetNativeSizesLength() const override;
virtual size_t SizeOfSourceWithComputedFallback(SizeOfState& aState)
const override;
virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,

View File

@ -565,5 +565,7 @@ interface imgIContainer : nsISupports
* Get the set of sizes the image can decode to natively.
*/
virtual nsresult GetNativeSizes(nsTArray<nsIntSize>& aNativeSizes) const = 0;
virtual size_t GetNativeSizesLength() const = 0;
%}
};

View File

@ -281,6 +281,12 @@ private:
for (const SurfaceMemoryCounter& counter : aCounter.Surfaces()) {
nsAutoCString surfacePathPrefix(aPathPrefix);
surfacePathPrefix.Append(counter.IsLocked() ? "locked/" : "unlocked/");
if (counter.IsFactor2()) {
surfacePathPrefix.Append("factor2/");
}
if (counter.CannotSubstitute()) {
surfacePathPrefix.Append("cannot_substitute/");
}
surfacePathPrefix.Append("surface(");
surfacePathPrefix.AppendInt(counter.Key().Size().width);
surfacePathPrefix.Append("x");

1
js/rust/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
target/

35
js/rust/CMakeLists.txt Normal file
View File

@ -0,0 +1,35 @@
project(rust-mozjs)
cmake_minimum_required(VERSION 2.6)
set(DUMMY ${CMAKE_BUILD_TYPE})
set(SOURCES
src/jsglue.cpp
)
include_directories($ENV{DEP_MOZJS_OUTDIR}/dist/include)
if(MSVC)
if(NOT "$ENV{CARGO_FEATURE_DEBUGMOZJS}" STREQUAL "")
add_definitions(-MDd -Od -DDEBUG -D_DEBUG)
else()
add_definitions(-MD)
endif()
add_definitions(-FI$ENV{DEP_MOZJS_OUTDIR}/js/src/js-confdefs.h)
add_definitions(-DWIN32)
add_definitions(-Zi -GR-)
else()
if(NOT "$ENV{CARGO_FEATURE_DEBUGMOZJS}" STREQUAL "")
add_definitions(-g -O0 -DDEBUG -D_DEBUG)
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_definitions(-Wno-c++0x-extensions -Wno-return-type-c-linkage -Wno-invalid-offsetof)
endif()
add_definitions(-fPIC -fno-rtti)
add_definitions(-std=c++11 -DJS_NO_JSVAL_JSID_STRUCT_TYPES)
add_definitions(-include $ENV{DEP_MOZJS_OUTDIR}/js/src/js-confdefs.h)
endif()
add_library(jsglue STATIC ${SOURCES})
install(TARGETS jsglue ARCHIVE DESTINATION lib)

494
js/rust/Cargo.lock generated Normal file
View File

@ -0,0 +1,494 @@
[root]
name = "js"
version = "0.1.4"
dependencies = [
"bindgen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cmake 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"mozjs_sys 0.0.0",
"num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "aho-corasick"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ansi_term"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "aster"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atty"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bindgen"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"which 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cexpr"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nom 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cfg-if"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clang-sys"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
"libloading 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clap"
version = "2.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cmake"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "env_logger"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gcc"
version = "0.3.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "glob"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "heapsize"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libloading"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libz-sys"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mozjs_sys"
version = "0.0.0"
dependencies = [
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
"libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nom"
version = "1.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "num-traits"
version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "num_cpus"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pkg-config"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quasi"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quasi_codegen"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "strsim"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syntex"
version = "0.58.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syntex_errors"
version = "0.58.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syntex_pos"
version = "0.58.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syntex_syntax"
version = "0.58.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "term"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "term_size"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "textwrap"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread-id"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"thread-id 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-segmentation"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-width"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unreachable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "utf8-ranges"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vcpkg"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "which"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
"checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0"
"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
"checksum bindgen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "33024f55a754d920637461adf87fb485702a69bdf7ac1d307b7e18da93bae505"
"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
"checksum cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393a5f0088efbe41f9d1fcd062f24e83c278608420e62109feb2c8abee07de7d"
"checksum cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c47d456a36ebf0536a6705c83c1cbbcb9255fbc1d905a6ded104f479268a29"
"checksum clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "611ec2e3a7623afd8a8c0d027887b6b55759d894abbf5fe11b9dc11b50d5b49a"
"checksum clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "867a885995b4184be051b70a592d4d70e32d7a188db6e8dff626af286a962771"
"checksum cmake 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b8ebbb35d3dc9cd09497168f33de1acb79b265d350ab0ac34133b98f8509af1f"
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
"checksum gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)" = "120d07f202dcc3f72859422563522b66fe6463a4c513df062874daad05f85f0a"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf"
"checksum libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)" = "38f5c2b18a287cf78b4097db62e20f43cace381dc76ae5c0a3073067f78b7ddc"
"checksum libloading 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "be99f814beb3e9503a786a592c909692bb6d4fc5a695f6ed7987223acfbd5194"
"checksum libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd64ef8ee652185674455c1d450b83cbc8ad895625d543b5324d923f82e4d8"
"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
"checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4"
"checksum nom 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce"
"checksum num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "1708c0628602a98b52fad936cf3edb9a107af06e52e49fdf0707e884456a6af6"
"checksum num_cpus 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83df569ffd47dbf87f36bead512632f89e90882b8e7a14286d0471daf6b72de9"
"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3"
"checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4"
"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b"
"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
"checksum syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8f5e3aaa79319573d19938ea38d068056b826db9883a5d47f86c1cecc688f0e"
"checksum syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "867cc5c2d7140ae7eaad2ae9e8bf39cb18a67ca651b7834f88d46ca98faadb9c"
"checksum syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13ad4762fe52abc9f4008e85c4fb1b1fe3aa91ccb99ff4826a439c7c598e1047"
"checksum syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e0e4dbae163dd98989464c23dd503161b338790640e11537686f2ef0f25c791"
"checksum term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d168af3930b369cfe245132550579d47dfd873d69470755a19c2c6568dbbd989"
"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
"checksum textwrap 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f86300c3e7416ee233abd7cda890c492007a3980f941f79185c753a701257167"
"checksum thread-id 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8df7875b676fddfadffd96deea3b1124e5ede707d4884248931077518cf1f773"
"checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7"
"checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3"
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b"
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum which 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d238435618c0f298d2d75596c2d4fa7d4ea469c0c1c3ff824737ed50ad5ab61c"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"

50
js/rust/Cargo.toml Normal file
View File

@ -0,0 +1,50 @@
[package]
name = "js"
version = "0.1.4"
authors = ["The Servo Project Developers"]
build = "build.rs"
license = "MPL-2.0"
[build-dependencies]
env_logger = "0.4"
log = "0.3"
bindgen = "0.30.0"
cmake = "0.1"
glob = "0.2.11"
[[test]]
name = "callback"
[[test]]
name = "enumerate"
[[test]]
name = "evaluate"
[[test]]
name = "panic"
[[test]]
name = "rooting"
[[test]]
name = "runtime"
[[test]]
name = "typedarray"
[[test]]
name = "stack_limit"
[[test]]
name = "vec_conversion"
[lib]
doctest = false
[features]
debugmozjs = ['mozjs_sys/debugmozjs']
promises = ['mozjs_sys/promises']
nonzero = []
[dependencies.mozjs_sys]
path = "../src"
[dependencies]
lazy_static = "0.2.1"
libc = "0.2"
log = "0.3"
heapsize = "0.4"
num-traits = "0.1.32"

46
js/rust/README.md Normal file
View File

@ -0,0 +1,46 @@
# The `js` Crate: Rust Bindings to SpiderMonkey
[User Documentation](http://doc.servo.org/js/)
## Building
To build a release version of SpiderMonkey and the Rust code with optimizations
enabled:
```
$ cargo build --release
```
To build with SpiderMonkey's DEBUG checks and assertions:
```
$ cargo build --features debugmozjs
```
Raw FFI bindings to JSAPI are machine generated with
[`rust-lang-nursery/rust-bindgen`][bindgen], and requires libclang >= 3.9. See
`./build.rs` for details.
[bindgen]: https://github.com/rust-lang-nursery/rust-bindgen
## Cargo Features
* `debugmozjs`: Create a DEBUG build of SpiderMonkey with many extra assertions
enabled. This is decoupled from whether the crate and its Rust code is built
in debug or release mode.
* `promises`: Enable SpiderMonkey native promises.
* `nonzero`: Leverage the unstable `NonZero` type. Requires nightly Rust.
## Testing
Make sure to test both with and without the `debugmozjs` feature because various
structures have different sizes and get passed through functions differently at
the ABI level! At minimum, you should test with `debugmozjs` to get extra
assertions and checking.
```
$ cargo test
$ cargo test --features debugmozjs
```

468
js/rust/build.rs Normal file
View File

@ -0,0 +1,468 @@
/* 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/. */
extern crate bindgen;
extern crate cmake;
extern crate glob;
extern crate log;
extern crate env_logger;
use std::env;
use std::path;
fn main() {
log::set_logger(|max_log_level| {
use env_logger::Logger;
let env_logger = Logger::new();
max_log_level.set(env_logger.filter());
Box::new(env_logger)
}).expect("Failed to set logger.");
build_jsapi_bindings();
build_jsglue_cpp();
}
/// Build the ./src/jsglue.cpp file containing C++ glue methods built on top of
/// JSAPI.
fn build_jsglue_cpp() {
let dst = cmake::Config::new(".").build();
println!("cargo:rustc-link-search=native={}/lib", dst.display());
println!("cargo:rustc-link-lib=static=jsglue");
println!("cargo:rerun-if-changed=src/jsglue.cpp");
}
/// Find the public include directory within our mozjs-sys crate dependency.
fn get_mozjs_include_dir() -> path::PathBuf {
let out_dir = env::var("OUT_DIR")
.expect("cargo should invoke us with the OUT_DIR env var set");
let mut target_build_dir = path::PathBuf::from(out_dir);
target_build_dir.push("../../");
let mut include_dir_glob = target_build_dir.display().to_string();
include_dir_glob.push_str("mozjs_sys-*/out/dist/include");
let entries = glob::glob(&include_dir_glob)
.expect("Should find entries for mozjs-sys include dir");
for entry in entries {
if let Ok(path) = entry {
return path.canonicalize()
.expect("Should canonicalize include path");
}
}
panic!("Should have found either a mozjs_sys in target/debug or in target/release");
}
/// Invoke bindgen on the JSAPI headers to produce raw FFI bindings for use from
/// Rust.
///
/// To add or remove which functions, types, and variables get bindings
/// generated, see the `const` configuration variables below.
fn build_jsapi_bindings() {
let mut builder = bindgen::builder()
.rust_target(bindgen::RustTarget::Stable_1_19)
.header("./etc/wrapper.hpp")
.raw_line("pub use self::root::*;")
.enable_cxx_namespaces();
if cfg!(feature = "debugmozjs") {
builder = builder
.clang_arg("-DJS_GC_ZEAL")
.clang_arg("-DDEBUG")
.clang_arg("-DJS_DEBUG");
}
let include_dir = get_mozjs_include_dir();
let include_dir = include_dir.to_str()
.expect("Path to mozjs include dir should be utf-8");
builder = builder.clang_arg("-I");
builder = builder.clang_arg(include_dir);
for ty in UNSAFE_IMPL_SYNC_TYPES {
builder = builder.raw_line(format!("unsafe impl Sync for {} {{}}", ty));
}
for extra in EXTRA_CLANG_FLAGS {
builder = builder.clang_arg(*extra);
}
for ty in WHITELIST_TYPES {
builder = builder.whitelisted_type(ty);
}
for var in WHITELIST_VARS {
builder = builder.whitelisted_var(var);
}
for func in WHITELIST_FUNCTIONS {
builder = builder.whitelisted_function(func);
}
for ty in OPAQUE_TYPES {
builder = builder.opaque_type(ty);
}
for ty in BLACKLIST_TYPES {
builder = builder.hide_type(ty);
}
let bindings = builder.generate()
.expect("Should generate JSAPI bindings OK");
let out = path::PathBuf::from(env::var("OUT_DIR").unwrap());
if cfg!(feature = "debugmozjs") {
bindings.write_to_file(out.join("jsapi_debug.rs"))
.expect("Should write bindings to file OK");
} else {
bindings.write_to_file(out.join("jsapi.rs"))
.expect("Should write bindings to file OK");
}
println!("cargo:rerun-if-changed=etc/wrapper.hpp");
}
/// JSAPI types for which we should implement `Sync`.
const UNSAFE_IMPL_SYNC_TYPES: &'static [&'static str] = &[
"JSClass",
"JSFunctionSpec",
"JSNativeWrapper",
"JSPropertySpec",
"JSTypedMethodJitInfo",
];
/// Flags passed through bindgen directly to Clang.
const EXTRA_CLANG_FLAGS: &'static [&'static str] = &[
"-x", "c++",
"-std=c++14",
"-DRUST_BINDGEN",
];
/// Types which we want to generate bindings for (and every other type they
/// transitively use).
const WHITELIST_TYPES: &'static [&'static str] = &[
"JS::AutoCheckCannotGC",
"JS::AutoIdVector",
"JS::AutoObjectVector",
"JS::CallArgs",
"js::Class",
"JS::CompartmentOptions",
"JS::ContextOptions",
"js::DOMCallbacks",
"js::DOMProxyShadowsResult",
"js::ESClass",
"JS::ForOfIterator",
"JS::Handle",
"JS::HandleId",
"JS::HandleObject",
"JS::HandleString",
"JS::HandleValue",
"JS::HandleValueArray",
"JS::IsAcceptableThis",
"JSAutoCompartment",
"JSAutoStructuredCloneBuffer",
"JSClass",
"JSClassOps",
"JSContext",
"JSErrNum",
"JSErrorCallback",
"JSErrorFormatString",
"JSErrorReport",
"JSExnType",
"JSFlatString",
"JSFunction",
"JSFunctionSpec",
"JS::GCDescription",
"JSGCInvocationKind",
"JSGCMode",
"JSGCParamKey",
"JS::GCProgress",
"JSGCStatus",
"JSJitCompilerOption",
"JSJitGetterCallArgs",
"JSJitMethodCallArgs",
"JSJitSetterCallArgs",
"JSNativeWrapper",
"JSPropertySpec",
"JSProtoKey",
"JSObject",
"JSString",
"JSStructuredCloneReader",
"JSStructuredCloneWriter",
"JSScript",
"JSType",
"JSTypedMethodJitInfo",
"JSValueTag",
"JSValueType",
"JSVersion",
"JSWrapObjectCallbacks",
"jsid",
"JS::Latin1Char",
"JS::detail::MaybeWrapped",
"JS::MutableHandle",
"JS::MutableHandleObject",
"JS::MutableHandleValue",
"JS::NativeImpl",
"js::ObjectOps",
"JS::ObjectOpResult",
"JS::PromiseState",
"JS::PropertyDescriptor",
"JS::Rooted",
"JS::RootedObject",
"JS::RootingContext",
"JS::RootKind",
"js::Scalar::Type",
"JS::ServoSizes",
"js::shadow::Object",
"js::shadow::ObjectGroup",
"JS::SourceBufferHolder",
"JSStructuredCloneCallbacks",
"JS::Symbol",
"JS::SymbolCode",
"JS::TraceKind",
"JS::TransferableOwnership",
"JS::Value",
"JS::WarningReporter",
"JS::shadow::Zone",
"JS::Zone",
];
/// Global variables we want to generate bindings to.
const WHITELIST_VARS: &'static [&'static str] = &[
"JS_STRUCTURED_CLONE_VERSION",
"JSCLASS_.*",
"JSFUN_.*",
"JSID_VOID",
"JSITER_.*",
"JSPROP_.*",
"JS::FalseHandleValue",
"JS::NullHandleValue",
"JS::TrueHandleValue",
"JS::UndefinedHandleValue",
];
/// Functions we want to generate bindings to.
const WHITELIST_FUNCTIONS: &'static [&'static str] = &[
"INTERNED_STRING_TO_JSID",
"JS_AddExtraGCRootsTracer",
"JS_AddInterruptCallback",
"JS::AddPromiseReactions",
"js::AddRawValueRoot",
"JS_AlreadyHasOwnPropertyById",
"JS_AtomizeAndPinString",
"js::AssertSameCompartment",
"JS::Call",
"JS_CallFunctionValue",
"JS::CallOriginalPromiseThen",
"JS::CallOriginalPromiseResolve",
"JS::CallOriginalPromiseReject",
"JS::CompileFunction",
"JS::Construct",
"JS::ContextOptionsRef",
"JS_CopyPropertiesFrom",
"JS::CurrentGlobalOrNull",
"JS_DeletePropertyById",
"js::detail::IsWindowSlow",
"JS::Evaluate",
"JS_ForwardGetPropertyTo",
"JS_ForwardSetPropertyTo",
"JS::GCTraceKindToAscii",
"js::GetArrayBufferLengthAndData",
"js::GetArrayBufferViewLengthAndData",
"JS_GetErrorPrototype",
"js::GetFunctionNativeReserved",
"JS_GetFunctionPrototype",
"js::GetGlobalForObjectCrossCompartment",
"JS_GetIteratorPrototype",
"js::GetObjectProto",
"JS_GetObjectPrototype",
"JS_GetObjectRuntime",
"JS_GetOwnPropertyDescriptorById",
"JS::GetPromiseState",
"JS_GetPropertyDescriptorById",
"js::GetPropertyKeys",
"JS_GetPrototype",
"JS_GetRuntime",
"js::GetStaticPrototype",
"JS_HasOwnPropertyById",
"JS_HasProperty",
"JS_HasPropertyById",
"JS::HeapObjectPostBarrier",
"JS::HeapValuePostBarrier",
"JS_InitializePropertiesFromCompatibleNativeObject",
"JS::InitSelfHostedCode",
"JS::IsConstructor",
"JS::IsPromiseObject",
"JS_BeginRequest",
"JS_ClearPendingException",
"JS_DefineElement",
"JS_DefineFunction",
"JS_DefineFunctions",
"JS_DefineProperties",
"JS_DefineProperty",
"JS_DefinePropertyById",
"JS_DefineUCProperty",
"JS::detail::InitWithFailureDiagnostic",
"JS_DestroyContext",
"JS::DisableIncrementalGC",
"js::Dump.*",
"JS_EncodeStringToUTF8",
"JS_EndRequest",
"JS_EnterCompartment",
"JS_EnumerateStandardClasses",
"JS_ErrorFromException",
"JS_FireOnNewGlobalObject",
"JS_GC",
"JS_GetArrayBufferData",
"JS_GetArrayBufferViewType",
"JS_GetFloat32ArrayData",
"JS_GetFloat64ArrayData",
"JS_GetFunctionObject",
"JS_GetGCParameter",
"JS_GetInt16ArrayData",
"JS_GetInt32ArrayData",
"JS_GetInt8ArrayData",
"JS_GetLatin1StringCharsAndLength",
"JS_GetParentRuntime",
"JS_GetPendingException",
"JS_GetProperty",
"JS_GetPropertyById",
"js::GetPropertyKeys",
"JS_GetPrototype",
"JS_GetReservedSlot",
"JS::GetScriptedCallerGlobal",
"JS_GetTwoByteStringCharsAndLength",
"JS_GetUint16ArrayData",
"JS_GetUint32ArrayData",
"JS_GetUint8ArrayData",
"JS_GetUint8ClampedArrayData",
"JS::GetWellKnownSymbol",
"JS_GlobalObjectTraceHook",
"JS::HideScriptedCaller",
"JS_InitStandardClasses",
"JS_IsArrayObject",
"JS_IsExceptionPending",
"JS_IsGlobalObject",
"JS::IsCallable",
"JS_LeaveCompartment",
"JS_LinkConstructorAndPrototype",
"JS_MayResolveStandardClass",
"JS_NewArrayBuffer",
"JS_NewArrayObject",
"JS_NewContext",
"JS_NewFloat32Array",
"JS_NewFloat64Array",
"JS_NewFunction",
"js::NewFunctionWithReserved",
"JS_NewGlobalObject",
"JS_NewInt16Array",
"JS_NewInt32Array",
"JS_NewInt8Array",
"JS_NewObject",
"JS_NewObjectWithGivenProto",
"JS_NewObjectWithoutMetadata",
"JS_NewObjectWithUniqueType",
"JS_NewPlainObject",
"JS::NewPromiseObject",
"JS_NewStringCopyN",
"JS_NewUCStringCopyN",
"JS_NewUint16Array",
"JS_NewUint32Array",
"JS_NewUint8Array",
"JS_NewUint8ClampedArray",
"js::ObjectClassName",
"JS_ObjectIsDate",
"JS_ParseJSON",
"JS_ReadBytes",
"JS_ReadStructuredClone",
"JS_ReadUint32Pair",
"js::RemoveRawValueRoot",
"JS_ReportErrorASCII",
"JS_ReportErrorNumberUTF8",
"JS_RequestInterruptCallback",
"JS_ResolveStandardClass",
"JS_SameValue",
"js::SetDOMCallbacks",
"js::SetDOMProxyInformation",
"JS::SetEnqueuePromiseJobCallback",
"js::SetFunctionNativeReserved",
"JS_SetGCCallback",
"JS::SetGCSliceCallback",
"JS_SetGCParameter",
"JS_SetGCZeal",
"JS::SetGetIncumbentGlobalCallback",
"JS_SetGlobalJitCompilerOption",
"JS_SetImmutablePrototype",
"JS_SetNativeStackQuota",
"JS_SetOffthreadIonCompilationEnabled",
"JS_SetParallelParsingEnabled",
"JS_SetPendingException",
"js::SetPreserveWrapperCallback",
"JS_SetPrototype",
"js::SetWindowProxy",
"js::SetWindowProxyClass",
"JS_SetProperty",
"JS_SetReservedSlot",
"JS_SetWrapObjectCallbacks",
"JS_ShutDown",
"JS_SplicePrototype",
"JS_StrictPropertyStub",
"JS_StringEqualsAscii",
"JS_StringHasLatin1Chars",
"JS_WrapObject",
"JS_WrapValue",
"JS_WriteBytes",
"JS_WriteStructuredClone",
"JS_WriteUint32Pair",
"JS::ResolvePromise",
"JS::RejectPromise",
"JS::SetWarningReporter",
"js::ToBooleanSlow",
"js::ToInt32Slow",
"js::ToInt64Slow",
"js::ToNumberSlow",
"js::ToStringSlow",
"js::ToUint16Slow",
"js::ToUint32Slow",
"js::ToUint64Slow",
"JS_TransplantObject",
"js::detail::ToWindowProxyIfWindowSlow",
"JS::UnhideScriptedCaller",
"js::UnwrapArrayBuffer",
"js::UnwrapArrayBufferView",
"js::UnwrapFloat32Array",
"js::UnwrapFloat64Array",
"js::UnwrapInt16Array",
"js::UnwrapInt32Array",
"js::UnwrapInt8Array",
"js::UnwrapUint16Array",
"js::UnwrapUint32Array",
"js::UnwrapUint8Array",
"js::UnwrapUint8ClampedArray",
];
/// Types that should be treated as an opaque blob of bytes whenever they show
/// up within a whitelisted type.
///
/// These are types which are too tricky for bindgen to handle, and/or use C++
/// features that don't have an equivalent in rust, such as partial template
/// specialization.
const OPAQUE_TYPES: &'static [&'static str] = &[
"JS::ReadOnlyCompileOptions",
"mozilla::BufferList",
"mozilla::UniquePtr.*",
"JS::Rooted<JS::Auto.*Vector.*>",
];
/// Types for which we should NEVER generate bindings, even if it is used within
/// a type or function signature that we are generating bindings for.
const BLACKLIST_TYPES: &'static [&'static str] = &[
// We provide our own definition because we need to express trait bounds in
// the definition of the struct to make our Drop implementation correct.
"JS::Heap",
];

23
js/rust/etc/wrapper.hpp Normal file
View File

@ -0,0 +1,23 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 <stdint.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
typedef uint32_t HashNumber;
#include "jsfriendapi.h"
#include "js/Conversions.h"
#include "js/Initialization.h"
#include "js/MemoryMetrics.h"
// Replacements for types that are too difficult for rust-bindgen.
/// <div rustbindgen replaces="JS::detail::MaybeWrapped" />
template <typename T>
using replaces_MaybeWrapped = T;

56
js/rust/src/ac.rs Normal file
View File

@ -0,0 +1,56 @@
use jsapi::root::*;
#[cfg(feature = "debugmozjs")]
use std::ptr;
#[derive(Debug)]
pub struct AutoCompartment(JSAutoCompartment);
impl AutoCompartment {
#[cfg(feature = "debugmozjs")]
pub unsafe fn with_obj(cx: *mut JSContext,
target: *mut JSObject)
-> AutoCompartment
{
let mut notifier = mozilla::detail::GuardObjectNotifier {
mStatementDone: ptr::null_mut(),
};
AutoCompartment(
JSAutoCompartment::new(
cx,
target,
&mut notifier as *mut _))
}
#[cfg(not(feature = "debugmozjs"))]
pub unsafe fn with_obj(cx: *mut JSContext,
target: *mut JSObject)
-> AutoCompartment
{
AutoCompartment(JSAutoCompartment::new(cx, target))
}
#[cfg(feature = "debugmozjs")]
pub unsafe fn with_script(cx: *mut JSContext,
target: *mut JSScript)
-> AutoCompartment
{
let mut notifier = mozilla::detail::GuardObjectNotifier {
mStatementDone: ptr::null_mut(),
};
AutoCompartment(
JSAutoCompartment::new1(
cx,
target,
&mut notifier as *mut _))
}
#[cfg(not(feature = "debugmozjs"))]
pub unsafe fn with_script(cx: *mut JSContext,
target: *mut JSScript)
-> AutoCompartment
{
AutoCompartment(JSAutoCompartment::new1(cx, target))
}
}

679
js/rust/src/conversions.rs Normal file
View File

@ -0,0 +1,679 @@
/* 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/. */
//! Conversions of Rust values to and from `JSVal`.
//!
//! | IDL type | Type |
//! |-------------------------|----------------------------------|
//! | any | `JSVal` |
//! | boolean | `bool` |
//! | byte | `i8` |
//! | octet | `u8` |
//! | short | `i16` |
//! | unsigned short | `u16` |
//! | long | `i32` |
//! | unsigned long | `u32` |
//! | long long | `i64` |
//! | unsigned long long | `u64` |
//! | unrestricted float | `f32` |
//! | float | `Finite<f32>` |
//! | unrestricted double | `f64` |
//! | double | `Finite<f64>` |
//! | USVString | `String` |
//! | object | `*mut JSObject` |
//! | nullable types | `Option<T>` |
//! | sequences | `Vec<T>` |
#![deny(missing_docs)]
#[cfg(feature = "nonzero")]
use core::nonzero::NonZero;
use error::throw_type_error;
use glue::RUST_JS_NumberValue;
use heap::Heap;
use jsapi::root::*;
use jsval::{BooleanValue, Int32Value, NullValue, UInt32Value, UndefinedValue};
use jsval::{ObjectValue, ObjectOrNullValue, StringValue};
use rust::{ToBoolean, ToInt32, ToInt64, ToNumber, ToUint16, ToUint32, ToUint64};
use rust::{ToString, maybe_wrap_object_or_null_value, maybe_wrap_value};
use libc;
use num_traits::{Bounded, Zero};
use std::borrow::Cow;
use std::rc::Rc;
use std::{ptr, slice};
trait As<O>: Copy {
fn cast(self) -> O;
}
macro_rules! impl_as {
($I:ty, $O:ty) => (
impl As<$O> for $I {
fn cast(self) -> $O {
self as $O
}
}
)
}
impl_as!(f64, u8);
impl_as!(f64, u16);
impl_as!(f64, u32);
impl_as!(f64, u64);
impl_as!(f64, i8);
impl_as!(f64, i16);
impl_as!(f64, i32);
impl_as!(f64, i64);
impl_as!(u8, f64);
impl_as!(u16, f64);
impl_as!(u32, f64);
impl_as!(u64, f64);
impl_as!(i8, f64);
impl_as!(i16, f64);
impl_as!(i32, f64);
impl_as!(i64, f64);
impl_as!(i32, i8);
impl_as!(i32, u8);
impl_as!(i32, i16);
impl_as!(u16, u16);
impl_as!(i32, i32);
impl_as!(u32, u32);
impl_as!(i64, i64);
impl_as!(u64, u64);
/// A trait to convert Rust types to `JSVal`s.
pub trait ToJSValConvertible {
/// Convert `self` to a `JSVal`. JSAPI failure causes a panic.
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue);
}
/// An enum to better support enums through FromJSValConvertible::from_jsval.
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum ConversionResult<T> {
/// Everything went fine.
Success(T),
/// Conversion failed, without a pending exception.
Failure(Cow<'static, str>),
}
impl<T> ConversionResult<T> {
/// Returns Some(value) if it is `ConversionResult::Success`.
pub fn get_success_value(&self) -> Option<&T> {
match *self {
ConversionResult::Success(ref v) => Some(v),
_ => None,
}
}
}
/// A trait to convert `JSVal`s to Rust types.
pub trait FromJSValConvertible: Sized {
/// Optional configurable behaviour switch; use () for no configuration.
type Config;
/// Convert `val` to type `Self`.
/// Optional configuration of type `T` can be passed as the `option`
/// argument.
/// If it returns `Err(())`, a JSAPI exception is pending.
/// If it returns `Ok(Failure(reason))`, there is no pending JSAPI exception.
unsafe fn from_jsval(cx: *mut JSContext,
val: JS::HandleValue,
option: Self::Config)
-> Result<ConversionResult<Self>, ()>;
}
/// Behavior for converting out-of-range integers.
#[derive(PartialEq, Eq, Clone)]
pub enum ConversionBehavior {
/// Wrap into the integer's range.
Default,
/// Throw an exception.
EnforceRange,
/// Clamp into the integer's range.
Clamp,
}
/// Try to cast the number to a smaller type, but
/// if it doesn't fit, it will return an error.
unsafe fn enforce_range<D>(cx: *mut JSContext, d: f64) -> Result<ConversionResult<D>, ()>
where D: Bounded + As<f64>,
f64: As<D>
{
if d.is_infinite() {
throw_type_error(cx, "value out of range in an EnforceRange argument");
return Err(());
}
let rounded = d.round();
if D::min_value().cast() <= rounded && rounded <= D::max_value().cast() {
Ok(ConversionResult::Success(rounded.cast()))
} else {
throw_type_error(cx, "value out of range in an EnforceRange argument");
Err(())
}
}
/// Try to cast the number to a smaller type, but if it doesn't fit,
/// round it to the MAX or MIN of the source type before casting it to
/// the destination type.
fn clamp_to<D>(d: f64) -> D
where D: Bounded + As<f64> + Zero,
f64: As<D>
{
if d.is_nan() {
D::zero()
} else if d > D::max_value().cast() {
D::max_value()
} else if d < D::min_value().cast() {
D::min_value()
} else {
d.cast()
}
}
// https://heycam.github.io/webidl/#es-void
impl ToJSValConvertible for () {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(UndefinedValue());
}
}
impl FromJSValConvertible for JS::HandleValue {
type Config = ();
#[inline]
unsafe fn from_jsval(cx: *mut JSContext,
value: JS::HandleValue,
_option: ())
-> Result<ConversionResult<JS::HandleValue>, ()> {
if value.is_object() {
js::AssertSameCompartment(cx, value.to_object());
}
Ok(ConversionResult::Success(value))
}
}
impl FromJSValConvertible for JS::Value {
type Config = ();
unsafe fn from_jsval(_cx: *mut JSContext,
value: JS::HandleValue,
_option: ())
-> Result<ConversionResult<JS::Value>, ()> {
Ok(ConversionResult::Success(value.get()))
}
}
impl FromJSValConvertible for Heap<JS::Value> {
type Config = ();
unsafe fn from_jsval(_cx: *mut JSContext,
value: JS::HandleValue,
_option: ())
-> Result<ConversionResult<Self>, ()> {
Ok(ConversionResult::Success(Heap::<JS::Value>::new(value.get())))
}
}
impl ToJSValConvertible for JS::Value {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(*self);
maybe_wrap_value(cx, rval);
}
}
impl ToJSValConvertible for JS::HandleValue {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(self.get());
maybe_wrap_value(cx, rval);
}
}
impl ToJSValConvertible for Heap<JS::Value> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(self.get());
maybe_wrap_value(cx, rval);
}
}
#[inline]
unsafe fn convert_int_from_jsval<T, M>(cx: *mut JSContext, value: JS::HandleValue,
option: ConversionBehavior,
convert_fn: unsafe fn(*mut JSContext, JS::HandleValue) -> Result<M, ()>)
-> Result<ConversionResult<T>, ()>
where T: Bounded + Zero + As<f64>,
M: Zero + As<T>,
f64: As<T>
{
match option {
ConversionBehavior::Default => Ok(ConversionResult::Success(try!(convert_fn(cx, value)).cast())),
ConversionBehavior::EnforceRange => enforce_range(cx, try!(ToNumber(cx, value))),
ConversionBehavior::Clamp => Ok(ConversionResult::Success(clamp_to(try!(ToNumber(cx, value))))),
}
}
// https://heycam.github.io/webidl/#es-boolean
impl ToJSValConvertible for bool {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(BooleanValue(*self));
}
}
// https://heycam.github.io/webidl/#es-boolean
impl FromJSValConvertible for bool {
type Config = ();
unsafe fn from_jsval(_cx: *mut JSContext, val: JS::HandleValue, _option: ()) -> Result<ConversionResult<bool>, ()> {
Ok(ToBoolean(val)).map(ConversionResult::Success)
}
}
// https://heycam.github.io/webidl/#es-byte
impl ToJSValConvertible for i8 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(Int32Value(*self as i32));
}
}
// https://heycam.github.io/webidl/#es-byte
impl FromJSValConvertible for i8 {
type Config = ConversionBehavior;
unsafe fn from_jsval(cx: *mut JSContext,
val: JS::HandleValue,
option: ConversionBehavior)
-> Result<ConversionResult<i8>, ()> {
convert_int_from_jsval(cx, val, option, ToInt32)
}
}
// https://heycam.github.io/webidl/#es-octet
impl ToJSValConvertible for u8 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(Int32Value(*self as i32));
}
}
// https://heycam.github.io/webidl/#es-octet
impl FromJSValConvertible for u8 {
type Config = ConversionBehavior;
unsafe fn from_jsval(cx: *mut JSContext,
val: JS::HandleValue,
option: ConversionBehavior)
-> Result<ConversionResult<u8>, ()> {
convert_int_from_jsval(cx, val, option, ToInt32)
}
}
// https://heycam.github.io/webidl/#es-short
impl ToJSValConvertible for i16 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(Int32Value(*self as i32));
}
}
// https://heycam.github.io/webidl/#es-short
impl FromJSValConvertible for i16 {
type Config = ConversionBehavior;
unsafe fn from_jsval(cx: *mut JSContext,
val: JS::HandleValue,
option: ConversionBehavior)
-> Result<ConversionResult<i16>, ()> {
convert_int_from_jsval(cx, val, option, ToInt32)
}
}
// https://heycam.github.io/webidl/#es-unsigned-short
impl ToJSValConvertible for u16 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(Int32Value(*self as i32));
}
}
// https://heycam.github.io/webidl/#es-unsigned-short
impl FromJSValConvertible for u16 {
type Config = ConversionBehavior;
unsafe fn from_jsval(cx: *mut JSContext,
val: JS::HandleValue,
option: ConversionBehavior)
-> Result<ConversionResult<u16>, ()> {
convert_int_from_jsval(cx, val, option, ToUint16)
}
}
// https://heycam.github.io/webidl/#es-long
impl ToJSValConvertible for i32 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(Int32Value(*self));
}
}
// https://heycam.github.io/webidl/#es-long
impl FromJSValConvertible for i32 {
type Config = ConversionBehavior;
unsafe fn from_jsval(cx: *mut JSContext,
val: JS::HandleValue,
option: ConversionBehavior)
-> Result<ConversionResult<i32>, ()> {
convert_int_from_jsval(cx, val, option, ToInt32)
}
}
// https://heycam.github.io/webidl/#es-unsigned-long
impl ToJSValConvertible for u32 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(UInt32Value(*self));
}
}
// https://heycam.github.io/webidl/#es-unsigned-long
impl FromJSValConvertible for u32 {
type Config = ConversionBehavior;
unsafe fn from_jsval(cx: *mut JSContext,
val: JS::HandleValue,
option: ConversionBehavior)
-> Result<ConversionResult<u32>, ()> {
convert_int_from_jsval(cx, val, option, ToUint32)
}
}
// https://heycam.github.io/webidl/#es-long-long
impl ToJSValConvertible for i64 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(RUST_JS_NumberValue(*self as f64));
}
}
// https://heycam.github.io/webidl/#es-long-long
impl FromJSValConvertible for i64 {
type Config = ConversionBehavior;
unsafe fn from_jsval(cx: *mut JSContext,
val: JS::HandleValue,
option: ConversionBehavior)
-> Result<ConversionResult<i64>, ()> {
convert_int_from_jsval(cx, val, option, ToInt64)
}
}
// https://heycam.github.io/webidl/#es-unsigned-long-long
impl ToJSValConvertible for u64 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(RUST_JS_NumberValue(*self as f64));
}
}
// https://heycam.github.io/webidl/#es-unsigned-long-long
impl FromJSValConvertible for u64 {
type Config = ConversionBehavior;
unsafe fn from_jsval(cx: *mut JSContext,
val: JS::HandleValue,
option: ConversionBehavior)
-> Result<ConversionResult<u64>, ()> {
convert_int_from_jsval(cx, val, option, ToUint64)
}
}
// https://heycam.github.io/webidl/#es-float
impl ToJSValConvertible for f32 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(RUST_JS_NumberValue(*self as f64));
}
}
// https://heycam.github.io/webidl/#es-float
impl FromJSValConvertible for f32 {
type Config = ();
unsafe fn from_jsval(cx: *mut JSContext, val: JS::HandleValue, _option: ()) -> Result<ConversionResult<f32>, ()> {
let result = ToNumber(cx, val);
result.map(|f| f as f32).map(ConversionResult::Success)
}
}
// https://heycam.github.io/webidl/#es-double
impl ToJSValConvertible for f64 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(RUST_JS_NumberValue(*self));
}
}
// https://heycam.github.io/webidl/#es-double
impl FromJSValConvertible for f64 {
type Config = ();
unsafe fn from_jsval(cx: *mut JSContext, val: JS::HandleValue, _option: ()) -> Result<ConversionResult<f64>, ()> {
ToNumber(cx, val).map(ConversionResult::Success)
}
}
/// Converts a `JSString`, encoded in "Latin1" (i.e. U+0000-U+00FF encoded as 0x00-0xFF) into a
/// `String`.
pub unsafe fn latin1_to_string(cx: *mut JSContext, s: *mut JSString) -> String {
assert!(JS_StringHasLatin1Chars(s));
let mut length = 0;
let chars = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), s, &mut length);
assert!(!chars.is_null());
let chars = slice::from_raw_parts(chars, length as usize);
let mut s = String::with_capacity(length as usize);
s.extend(chars.iter().map(|&c| c as char));
s
}
// https://heycam.github.io/webidl/#es-USVString
impl ToJSValConvertible for str {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
let mut string_utf16: Vec<u16> = Vec::with_capacity(self.len());
string_utf16.extend(self.encode_utf16());
let jsstr = JS_NewUCStringCopyN(cx,
string_utf16.as_ptr(),
string_utf16.len() as libc::size_t);
if jsstr.is_null() {
panic!("JS_NewUCStringCopyN failed");
}
rval.set(StringValue(&*jsstr));
}
}
// https://heycam.github.io/webidl/#es-USVString
impl ToJSValConvertible for String {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
(**self).to_jsval(cx, rval);
}
}
// https://heycam.github.io/webidl/#es-USVString
impl FromJSValConvertible for String {
type Config = ();
unsafe fn from_jsval(cx: *mut JSContext, value: JS::HandleValue, _: ()) -> Result<ConversionResult<String>, ()> {
let jsstr = ToString(cx, value);
if jsstr.is_null() {
debug!("ToString failed");
return Err(());
}
if JS_StringHasLatin1Chars(jsstr) {
return Ok(latin1_to_string(cx, jsstr)).map(ConversionResult::Success);
}
let mut length = 0;
let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), jsstr, &mut length);
assert!(!chars.is_null());
let char_vec = slice::from_raw_parts(chars, length as usize);
Ok(String::from_utf16_lossy(char_vec)).map(ConversionResult::Success)
}
}
impl<T: ToJSValConvertible> ToJSValConvertible for Option<T> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
match self {
&Some(ref value) => value.to_jsval(cx, rval),
&None => rval.set(NullValue()),
}
}
}
impl<T: ToJSValConvertible> ToJSValConvertible for Rc<T> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
(**self).to_jsval(cx, rval)
}
}
impl<T: FromJSValConvertible> FromJSValConvertible for Option<T> {
type Config = T::Config;
unsafe fn from_jsval(cx: *mut JSContext,
value: JS::HandleValue,
option: T::Config)
-> Result<ConversionResult<Option<T>>, ()> {
if value.get().is_null_or_undefined() {
Ok(ConversionResult::Success(None))
} else {
Ok(match try!(FromJSValConvertible::from_jsval(cx, value, option)) {
ConversionResult::Success(v) => ConversionResult::Success(Some(v)),
ConversionResult::Failure(v) => ConversionResult::Failure(v),
})
}
}
}
// https://heycam.github.io/webidl/#es-sequence
impl<T: ToJSValConvertible> ToJSValConvertible for Vec<T> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
rooted!(in(cx) let js_array = JS_NewArrayObject1(cx, self.len() as libc::size_t));
assert!(!js_array.handle().is_null());
rooted!(in(cx) let mut val = UndefinedValue());
for (index, obj) in self.iter().enumerate() {
obj.to_jsval(cx, val.handle_mut());
assert!(JS_DefineElement(
cx,
js_array.handle(),
index as u32,
val.handle(),
JSPROP_ENUMERATE as _
));
}
rval.set(ObjectValue(js_array.handle().get()));
}
}
/// Rooting guard for the iterator field of ForOfIterator.
/// Behaves like RootedGuard (roots on creation, unroots on drop),
/// but borrows and allows access to the whole ForOfIterator, so
/// that methods on ForOfIterator can still be used through it.
struct ForOfIteratorGuard<'a> {
root: &'a mut JS::ForOfIterator
}
impl<'a> ForOfIteratorGuard<'a> {
fn new(cx: *mut JSContext, root: &'a mut JS::ForOfIterator) -> Self {
unsafe {
root.iterator.register_with_root_lists(cx);
}
ForOfIteratorGuard {
root: root
}
}
}
impl<'a> Drop for ForOfIteratorGuard<'a> {
fn drop(&mut self) {
unsafe {
self.root.iterator.remove_from_root_stack();
}
}
}
impl<C: Clone, T: FromJSValConvertible<Config=C>> FromJSValConvertible for Vec<T> {
type Config = C;
unsafe fn from_jsval(cx: *mut JSContext,
value: JS::HandleValue,
option: C)
-> Result<ConversionResult<Vec<T>>, ()> {
let mut iterator = JS::ForOfIterator {
cx_: cx,
iterator: JS::RootedObject::new_unrooted(),
index: ::std::u32::MAX, // NOT_ARRAY
};
let iterator = ForOfIteratorGuard::new(cx, &mut iterator);
let iterator = &mut *iterator.root;
if !iterator.init(value, JS::ForOfIterator_NonIterableBehavior::AllowNonIterable) {
return Err(())
}
if iterator.iterator.ptr.is_null() {
return Ok(ConversionResult::Failure("Value is not iterable".into()));
}
let mut ret = vec![];
loop {
let mut done = false;
rooted!(in(cx) let mut val = UndefinedValue());
if !iterator.next(val.handle_mut(), &mut done) {
return Err(())
}
if done {
break;
}
ret.push(match try!(T::from_jsval(cx, val.handle(), option.clone())) {
ConversionResult::Success(v) => v,
ConversionResult::Failure(e) => return Ok(ConversionResult::Failure(e)),
});
}
Ok(ret).map(ConversionResult::Success)
}
}
// https://heycam.github.io/webidl/#es-object
impl ToJSValConvertible for *mut JSObject {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(ObjectOrNullValue(*self));
maybe_wrap_object_or_null_value(cx, rval);
}
}
// https://heycam.github.io/webidl/#es-object
#[cfg(feature = "nonzero")]
impl ToJSValConvertible for NonZero<*mut JSObject> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
use rust::maybe_wrap_object_value;
rval.set(ObjectValue(self.get()));
maybe_wrap_object_value(cx, rval);
}
}
// https://heycam.github.io/webidl/#es-object
impl ToJSValConvertible for Heap<*mut JSObject> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
rval.set(ObjectOrNullValue(self.get()));
maybe_wrap_object_or_null_value(cx, rval);
}
}

73
js/rust/src/error.rs Normal file
View File

@ -0,0 +1,73 @@
/* 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/. */
//! Functions to throw JavaScript exceptions from Rust.
#![deny(missing_docs)]
use jsapi::root::*;
use libc;
use std::ffi::CString;
use std::{mem, os, ptr};
/// Format string used to throw javascript errors.
static ERROR_FORMAT_STRING_STRING: [libc::c_char; 4] = [
'{' as libc::c_char,
'0' as libc::c_char,
'}' as libc::c_char,
0 as libc::c_char,
];
/// Format string struct used to throw `TypeError`s.
static mut TYPE_ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString {
name: b"RUSTMSG_TYPE_ERROR\0" as *const _ as *const libc::c_char,
format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char,
argCount: 1,
exnType: JSExnType::JSEXN_TYPEERR as i16,
};
/// Format string struct used to throw `RangeError`s.
static mut RANGE_ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString {
name: b"RUSTMSG_RANGE_ERROR\0" as *const _ as *const libc::c_char,
format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char,
argCount: 1,
exnType: JSExnType::JSEXN_RANGEERR as i16,
};
/// Callback used to throw javascript errors.
/// See throw_js_error for info about error_number.
unsafe extern "C" fn get_error_message(_user_ref: *mut os::raw::c_void,
error_number: libc::c_uint)
-> *const JSErrorFormatString {
let num: JSExnType = mem::transmute(error_number);
match num {
JSExnType::JSEXN_TYPEERR => &TYPE_ERROR_FORMAT_STRING as *const JSErrorFormatString,
JSExnType::JSEXN_RANGEERR => &RANGE_ERROR_FORMAT_STRING as *const JSErrorFormatString,
_ => panic!("Bad js error number given to get_error_message: {}",
error_number),
}
}
/// Helper fn to throw a javascript error with the given message and number.
/// Reuse the jsapi error codes to distinguish the error_number
/// passed back to the get_error_message callback.
/// c_uint is u32, so this cast is safe, as is casting to/from i32 from there.
unsafe fn throw_js_error(cx: *mut JSContext, error: &str, error_number: u32) {
let error = CString::new(error).unwrap();
JS_ReportErrorNumberUTF8(cx,
Some(get_error_message),
ptr::null_mut(),
error_number,
error.as_ptr());
}
/// Throw a `TypeError` with the given message.
pub unsafe fn throw_type_error(cx: *mut JSContext, error: &str) {
throw_js_error(cx, error, JSExnType::JSEXN_TYPEERR as u32);
}
/// Throw a `RangeError` with the given message.
pub unsafe fn throw_range_error(cx: *mut JSContext, error: &str) {
throw_js_error(cx, error, JSExnType::JSEXN_RANGEERR as u32);
}

348
js/rust/src/glue.rs Normal file
View File

@ -0,0 +1,348 @@
use jsapi::root::*;
use heap::Heap;
use std::os::raw::c_void;
pub enum Action { }
unsafe impl Sync for ProxyTraps {}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ProxyTraps {
pub enter: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
id: JS::HandleId,
action: Action,
bp: *mut bool)
-> bool>,
pub getOwnPropertyDescriptor:
::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
id: JS::HandleId,
desc: JS::MutableHandle<JS::PropertyDescriptor>)
-> bool>,
pub defineProperty:
::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
id: JS::HandleId,
desc: JS::Handle<JS::PropertyDescriptor>,
result: *mut JS::ObjectOpResult)
-> bool>,
pub ownPropertyKeys: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
props: *mut JS::AutoIdVector)
-> bool>,
pub delete_: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
id: JS::HandleId,
result: *mut JS::ObjectOpResult)
-> bool>,
pub enumerate: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
objp: JS::MutableHandleObject)
-> bool>,
pub getPrototypeIfOrdinary:
::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
isOrdinary: *mut bool,
protop: JS::MutableHandleObject)
-> bool>,
pub preventExtensions:
::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
result: *mut JS::ObjectOpResult)
-> bool>,
pub isExtensible: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
succeeded: *mut bool)
-> bool>,
pub has: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
id: JS::HandleId,
bp: *mut bool)
-> bool>,
pub get: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
receiver: JS::HandleValue,
id: JS::HandleId,
vp: JS::MutableHandleValue)
-> bool>,
pub set: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
id: JS::HandleId,
v: JS::HandleValue,
receiver: JS::HandleValue,
result: *mut JS::ObjectOpResult)
-> bool>,
pub call: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
args: *const JS::CallArgs)
-> bool>,
pub construct: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
args: *const JS::CallArgs)
-> bool>,
pub getPropertyDescriptor:
::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
id: JS::HandleId,
desc: JS::MutableHandle<JS::PropertyDescriptor>)
-> bool>,
pub hasOwn: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
id: JS::HandleId,
bp: *mut bool)
-> bool>,
pub getOwnEnumerablePropertyKeys:
::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
props: *mut JS::AutoIdVector)
-> bool>,
pub nativeCall: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
test: JS::IsAcceptableThis,
_impl: JS::NativeImpl,
args: JS::CallArgs)
-> bool>,
pub hasInstance: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
v: JS::MutableHandleValue,
bp: *mut bool)
-> bool>,
pub objectClassIs: ::std::option::Option<unsafe extern "C" fn(obj: JS::HandleObject,
classValue: js::ESClass,
cx: *mut JSContext)
-> bool>,
pub className: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject)
-> *const i8>,
pub fun_toString: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
indent: u32)
-> *mut JSString>,
pub boxedValue_unbox: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
proxy: JS::HandleObject,
vp: JS::MutableHandleValue)
-> bool>,
pub defaultValue: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
obj: JS::HandleObject,
hint: JSType,
vp: JS::MutableHandleValue)
-> bool>,
pub trace:
::std::option::Option<unsafe extern "C" fn(trc: *mut JSTracer, proxy: *mut JSObject)>,
pub finalize:
::std::option::Option<unsafe extern "C" fn(fop: *mut JSFreeOp, proxy: *mut JSObject)>,
pub objectMoved:
::std::option::Option<unsafe extern "C" fn(proxy: *mut JSObject, old: *const JSObject)>,
pub isCallable: ::std::option::Option<unsafe extern "C" fn(obj: *mut JSObject) -> bool>,
pub isConstructor: ::std::option::Option<unsafe extern "C" fn(obj: *mut JSObject) -> bool>,
}
impl ::std::default::Default for ProxyTraps {
fn default() -> ProxyTraps {
unsafe { ::std::mem::zeroed() }
}
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct WrapperProxyHandler {
pub mTraps: ProxyTraps,
}
impl ::std::default::Default for WrapperProxyHandler {
fn default() -> WrapperProxyHandler {
unsafe { ::std::mem::zeroed() }
}
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ForwardingProxyHandler {
pub mTraps: ProxyTraps,
pub mExtra: *const ::libc::c_void,
}
impl ::std::default::Default for ForwardingProxyHandler {
fn default() -> ForwardingProxyHandler {
unsafe { ::std::mem::zeroed() }
}
}
extern "C" {
pub fn InvokeGetOwnPropertyDescriptor(handler: *const ::libc::c_void,
cx: *mut JSContext,
proxy: JS::HandleObject,
id: JS::HandleId,
desc: JS::MutableHandle<JS::PropertyDescriptor>)
-> bool;
pub fn InvokeHasOwn(handler: *const ::libc::c_void,
cx: *mut JSContext,
proxy: JS::HandleObject,
id: JS::HandleId,
bp: *mut bool)
-> bool;
pub fn RUST_JS_NumberValue(d: f64) -> JS::Value;
pub fn RUST_FUNCTION_VALUE_TO_JITINFO(v: JS::Value) -> *const JSJitInfo;
pub fn CreateCallArgsFromVp(argc: u32, v: *mut JS::Value) -> JS::CallArgs;
pub fn CallJitGetterOp(info: *const JSJitInfo,
cx: *mut JSContext,
thisObj: JS::HandleObject,
specializedThis: *mut ::libc::c_void,
argc: u32,
vp: *mut JS::Value)
-> bool;
pub fn CallJitSetterOp(info: *const JSJitInfo,
cx: *mut JSContext,
thisObj: JS::HandleObject,
specializedThis: *mut ::libc::c_void,
argc: u32,
vp: *mut JS::Value)
-> bool;
pub fn CallJitMethodOp(info: *const JSJitInfo,
cx: *mut JSContext,
thisObj: JS::HandleObject,
specializedThis: *mut ::libc::c_void,
argc: u32,
vp: *mut JS::Value)
-> bool;
pub fn CreateProxyHandler(aTraps: *const ProxyTraps,
aExtra: *const ::libc::c_void)
-> *const ::libc::c_void;
pub fn CreateWrapperProxyHandler(aTraps: *const ProxyTraps) -> *const ::libc::c_void;
pub fn CreateRustJSPrincipal(origin: *const ::libc::c_void,
destroy: Option<unsafe extern "C" fn
(principal: *mut JSPrincipals)>,
write: Option<unsafe extern "C" fn
(cx: *mut JSContext,
writer: *mut JSStructuredCloneWriter)
-> bool>)
-> *mut JSPrincipals;
pub fn GetPrincipalOrigin(principal: *const JSPrincipals) -> *const ::libc::c_void;
pub fn GetCrossCompartmentWrapper() -> *const ::libc::c_void;
pub fn GetSecurityWrapper() -> *const ::libc::c_void;
pub fn NewCompileOptions(aCx: *mut JSContext,
aFile: *const ::libc::c_char,
aLine: u32)
-> *mut JS::ReadOnlyCompileOptions;
pub fn DeleteCompileOptions(aOpts: *mut JS::ReadOnlyCompileOptions);
pub fn NewProxyObject(aCx: *mut JSContext,
aHandler: *const ::libc::c_void,
aPriv: JS::HandleValue,
proto: *mut JSObject,
parent: *mut JSObject,
call: *mut JSObject,
construct: *mut JSObject)
-> *mut JSObject;
pub fn WrapperNew(aCx: *mut JSContext,
aObj: JS::HandleObject,
aHandler: *const ::libc::c_void,
aClass: *const JSClass,
aSingleton: bool)
-> *mut JSObject;
pub fn NewWindowProxy(aCx: *mut JSContext,
aObj: JS::HandleObject,
aHandler: *const ::libc::c_void)
-> *mut JSObject;
pub fn GetWindowProxyClass() -> *const js::Class;
pub fn GetProxyPrivate(obj: *mut JSObject) -> JS::Value;
pub fn SetProxyPrivate(obj: *mut JSObject, private: *const JS::Value);
pub fn GetProxyReservedSlot(obj: *mut JSObject, slot: u32) -> JS::Value;
pub fn SetProxyReservedSlot(obj: *mut JSObject, slot: u32, val: *const JS::Value);
pub fn RUST_JSID_IS_INT(id: JS::HandleId) -> bool;
pub fn RUST_JSID_TO_INT(id: JS::HandleId) -> i32;
pub fn int_to_jsid(i: i32) -> jsid;
pub fn RUST_JSID_IS_STRING(id: JS::HandleId) -> bool;
pub fn RUST_JSID_TO_STRING(id: JS::HandleId) -> *mut JSString;
pub fn RUST_SYMBOL_TO_JSID(sym: *mut JS::Symbol) -> jsid;
pub fn RUST_SET_JITINFO(func: *mut JSFunction, info: *const JSJitInfo);
pub fn RUST_INTERNED_STRING_TO_JSID(cx: *mut JSContext, str: *mut JSString) -> jsid;
pub fn RUST_js_GetErrorMessage(userRef: *mut ::libc::c_void,
errorNumber: u32)
-> *const JSErrorFormatString;
pub fn IsProxyHandlerFamily(obj: *mut JSObject) -> u8;
pub fn GetProxyHandlerExtra(obj: *mut JSObject) -> *const ::libc::c_void;
pub fn GetProxyHandler(obj: *mut JSObject) -> *const ::libc::c_void;
pub fn ReportError(aCx: *mut JSContext, aError: *const i8);
pub fn IsWrapper(obj: *mut JSObject) -> bool;
pub fn UnwrapObject(obj: *mut JSObject, stopAtOuter: u8) -> *mut JSObject;
pub fn UncheckedUnwrapObject(obj: *mut JSObject, stopAtOuter: u8) -> *mut JSObject;
pub fn CreateAutoIdVector(cx: *mut JSContext) -> *mut JS::AutoIdVector;
pub fn AppendToAutoIdVector(v: *mut JS::AutoIdVector, id: jsid) -> bool;
pub fn SliceAutoIdVector(v: *const JS::AutoIdVector, length: *mut usize) -> *const jsid;
pub fn DestroyAutoIdVector(v: *mut JS::AutoIdVector);
pub fn CreateAutoObjectVector(aCx: *mut JSContext) -> *mut JS::AutoObjectVector;
pub fn AppendToAutoObjectVector(v: *mut JS::AutoObjectVector, obj: *mut JSObject) -> bool;
pub fn DeleteAutoObjectVector(v: *mut JS::AutoObjectVector);
pub fn CollectServoSizes(rt: *mut JSRuntime, sizes: *mut JS::ServoSizes) -> bool;
pub fn CallIdTracer(trc: *mut JSTracer, idp: *mut Heap<jsid>, name: *const ::libc::c_char);
pub fn CallValueTracer(trc: *mut JSTracer,
valuep: *mut Heap<JS::Value>,
name: *const ::libc::c_char);
pub fn CallObjectTracer(trc: *mut JSTracer,
objp: *mut Heap<*mut JSObject>,
name: *const ::libc::c_char);
pub fn CallStringTracer(trc: *mut JSTracer,
strp: *mut Heap<*mut JSString>,
name: *const ::libc::c_char);
pub fn CallScriptTracer(trc: *mut JSTracer,
scriptp: *mut Heap<*mut JSScript>,
name: *const ::libc::c_char);
pub fn CallFunctionTracer(trc: *mut JSTracer,
funp: *mut Heap<*mut JSFunction>,
name: *const ::libc::c_char);
pub fn CallUnbarrieredObjectTracer(trc: *mut JSTracer,
objp: *mut *mut JSObject,
name: *const ::libc::c_char);
pub fn GetProxyHandlerFamily() -> *const c_void;
pub fn GetInt8ArrayLengthAndData(obj: *mut JSObject,
length: *mut u32,
isSharedMemory: *mut bool,
data: *mut *mut i8);
pub fn GetUint8ArrayLengthAndData(obj: *mut JSObject,
length: *mut u32,
isSharedMemory: *mut bool,
data: *mut *mut u8);
pub fn GetUint8ClampedArrayLengthAndData(obj: *mut JSObject,
length: *mut u32,
isSharedMemory: *mut bool,
data: *mut *mut u8);
pub fn GetInt16ArrayLengthAndData(obj: *mut JSObject,
length: *mut u32,
isSharedMemory: *mut bool,
data: *mut *mut i16);
pub fn GetUint16ArrayLengthAndData(obj: *mut JSObject,
length: *mut u32,
isSharedMemory: *mut bool,
data: *mut *mut u16);
pub fn GetInt32ArrayLengthAndData(obj: *mut JSObject,
length: *mut u32,
isSharedMemory: *mut bool,
data: *mut *mut i32);
pub fn GetUint32ArrayLengthAndData(obj: *mut JSObject,
length: *mut u32,
isSharedMemory: *mut bool,
data: *mut *mut u32);
pub fn GetFloat32ArrayLengthAndData(obj: *mut JSObject,
length: *mut u32,
isSharedMemory: *mut bool,
data: *mut *mut f32);
pub fn GetFloat64ArrayLengthAndData(obj: *mut JSObject,
length: *mut u32,
isSharedMemory: *mut bool,
data: *mut *mut f64);
pub fn NewJSAutoStructuredCloneBuffer(scope: JS::StructuredCloneScope,
callbacks: *const JSStructuredCloneCallbacks)
-> *mut JSAutoStructuredCloneBuffer;
pub fn DeleteJSAutoStructuredCloneBuffer(buf: *mut JSAutoStructuredCloneBuffer);
pub fn GetLengthOfJSStructuredCloneData(data: *mut JSStructuredCloneData) -> usize;
pub fn CopyJSStructuredCloneData(src: *mut JSStructuredCloneData, dest: *mut u8);
pub fn WriteBytesToJSStructuredCloneData(src: *const u8,
len: usize,
dest: *mut JSStructuredCloneData)
-> bool;
pub fn IsDebugBuild() -> bool;
}
#[test]
fn jsglue_cpp_configured_correctly() {
assert_eq!(cfg!(feature = "debugmozjs"), unsafe { IsDebugBuild() });
}

172
js/rust/src/heap.rs Normal file
View File

@ -0,0 +1,172 @@
use glue;
use heapsize::HeapSizeOf;
use jsapi::root::*;
use rust::GCMethods;
use std::cell::UnsafeCell;
use std::ptr;
/// Types that can be traced.
///
/// This trait is unsafe; if it is implemented incorrectly, the GC may end up
/// collecting objects that are still reachable.
pub unsafe trait Trace {
unsafe fn trace(&self, trc: *mut JSTracer);
}
/**
* The Heap<T> class is a heap-stored reference to a JS GC thing. All members of
* heap classes that refer to GC things should use Heap<T> (or possibly
* TenuredHeap<T>, described below).
*
* Heap<T> is an abstraction that hides some of the complexity required to
* maintain GC invariants for the contained reference. It uses operator
* overloading to provide a normal pointer interface, but notifies the GC every
* time the value it contains is updated. This is necessary for generational GC,
* which keeps track of all pointers into the nursery.
*
* Heap<T> instances must be traced when their containing object is traced to
* keep the pointed-to GC thing alive.
*
* Heap<T> objects should only be used on the heap. GC references stored on the
* C/C++ stack must use Rooted/Handle/MutableHandle instead.
*
* Type T must be a public GC pointer type.
*/
#[repr(C)]
#[derive(Debug)]
pub struct Heap<T: GCMethods + Copy> {
ptr: UnsafeCell<T>,
}
impl<T: GCMethods + Copy> Heap<T> {
pub fn new(v: T) -> Heap<T>
where Heap<T>: Default
{
let ptr = Heap::default();
ptr.set(v);
ptr
}
pub fn set(&self, v: T) {
unsafe {
let ptr = self.ptr.get();
let prev = *ptr;
*ptr = v;
T::post_barrier(ptr, prev, v);
}
}
pub fn get(&self) -> T {
unsafe {
*self.ptr.get()
}
}
pub unsafe fn get_unsafe(&self) -> *mut T {
self.ptr.get()
}
pub fn handle(&self) -> JS::Handle<T> {
unsafe {
JS::Handle::from_marked_location(self.ptr.get() as *const _)
}
}
pub fn handle_mut(&self) -> JS::MutableHandle<T> {
unsafe {
JS::MutableHandle::from_marked_location(self.ptr.get())
}
}
}
impl<T: GCMethods + Copy> Clone for Heap<T>
where Heap<T>: Default
{
fn clone(&self) -> Self {
Heap::new(self.get())
}
}
impl<T: GCMethods + Copy + PartialEq> PartialEq for Heap<T> {
fn eq(&self, other: &Self) -> bool {
self.get() == other.get()
}
}
impl<T> Default for Heap<*mut T>
where *mut T: GCMethods + Copy
{
fn default() -> Heap<*mut T> {
Heap {
ptr: UnsafeCell::new(ptr::null_mut())
}
}
}
impl Default for Heap<JS::Value> {
fn default() -> Heap<JS::Value> {
Heap {
ptr: UnsafeCell::new(JS::Value::default())
}
}
}
impl<T: GCMethods + Copy> Drop for Heap<T> {
fn drop(&mut self) {
unsafe {
let prev = self.ptr.get();
T::post_barrier(prev, *prev, T::initial());
}
}
}
// Creates a C string literal `$str`.
macro_rules! c_str {
($str:expr) => {
concat!($str, "\0").as_ptr() as *const ::std::os::raw::c_char
}
}
unsafe impl Trace for Heap<*mut JSFunction> {
unsafe fn trace(&self, trc: *mut JSTracer) {
glue::CallFunctionTracer(trc, self as *const _ as *mut Self, c_str!("function"));
}
}
unsafe impl Trace for Heap<*mut JSObject> {
unsafe fn trace(&self, trc: *mut JSTracer) {
glue::CallObjectTracer(trc, self as *const _ as *mut Self, c_str!("object"));
}
}
unsafe impl Trace for Heap<*mut JSScript> {
unsafe fn trace(&self, trc: *mut JSTracer) {
glue::CallScriptTracer(trc, self as *const _ as *mut Self, c_str!("script"));
}
}
unsafe impl Trace for Heap<*mut JSString> {
unsafe fn trace(&self, trc: *mut JSTracer) {
glue::CallStringTracer(trc, self as *const _ as *mut Self, c_str!("string"));
}
}
unsafe impl Trace for Heap<JS::Value> {
unsafe fn trace(&self, trc: *mut JSTracer) {
glue::CallValueTracer(trc, self as *const _ as *mut Self, c_str!("value"));
}
}
unsafe impl Trace for Heap<jsid> {
unsafe fn trace(&self, trc: *mut JSTracer) {
glue::CallIdTracer(trc, self as *const _ as *mut Self, c_str!("id"));
}
}
// This is measured properly by the heap measurement implemented in
// SpiderMonkey.
impl<T: Copy + GCMethods> HeapSizeOf for Heap<T> {
fn heap_size_of_children(&self) -> usize {
0
}
}

5
js/rust/src/jsapi.rs Normal file
View File

@ -0,0 +1,5 @@
#[cfg(feature = "debugmozjs")]
include!(concat!(env!("OUT_DIR"), "/jsapi_debug.rs"));
#[cfg(not(feature = "debugmozjs"))]
include!(concat!(env!("OUT_DIR"), "/jsapi.rs"));

940
js/rust/src/jsglue.cpp Normal file
View File

@ -0,0 +1,940 @@
/* 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/. */
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include "js-config.h"
#ifdef JS_DEBUG
// A hack for MFBT. Guard objects need this to work.
#define DEBUG 1
#endif
#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/Proxy.h"
#include "js/Class.h"
#include "jswrapper.h"
#include "js/MemoryMetrics.h"
#include "js/Principals.h"
#include "assert.h"
struct ProxyTraps {
bool (*enter)(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
js::BaseProxyHandler::Action action, bool *bp);
bool (*getOwnPropertyDescriptor)(JSContext *cx, JS::HandleObject proxy,
JS::HandleId id,
JS::MutableHandle<JS::PropertyDescriptor> desc);
bool (*defineProperty)(JSContext *cx, JS::HandleObject proxy,
JS::HandleId id,
JS::Handle<JS::PropertyDescriptor> desc,
JS::ObjectOpResult &result);
bool (*ownPropertyKeys)(JSContext *cx, JS::HandleObject proxy,
JS::AutoIdVector &props);
bool (*delete_)(JSContext *cx, JS::HandleObject proxy,
JS::HandleId id, JS::ObjectOpResult &result);
JSObject* (*enumerate)(JSContext *cx, JS::HandleObject proxy);
bool (*getPrototypeIfOrdinary)(JSContext *cx, JS::HandleObject proxy,
bool *isOrdinary, JS::MutableHandleObject protop);
// getPrototype
// setPrototype
// setImmutablePrototype
bool (*preventExtensions)(JSContext *cx, JS::HandleObject proxy,
JS::ObjectOpResult &result);
bool (*isExtensible)(JSContext *cx, JS::HandleObject proxy, bool *succeeded);
bool (*has)(JSContext *cx, JS::HandleObject proxy,
JS::HandleId id, bool *bp);
bool (*get)(JSContext *cx, JS::HandleObject proxy, JS::HandleValue receiver,
JS::HandleId id, JS::MutableHandleValue vp);
bool (*set)(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
JS::HandleValue v, JS::HandleValue receiver,
JS::ObjectOpResult &result);
bool (*call)(JSContext *cx, JS::HandleObject proxy,
const JS::CallArgs &args);
bool (*construct)(JSContext *cx, JS::HandleObject proxy,
const JS::CallArgs &args);
bool (*getPropertyDescriptor)(JSContext *cx, JS::HandleObject proxy,
JS::HandleId id,
JS::MutableHandle<JS::PropertyDescriptor> desc);
bool (*hasOwn)(JSContext *cx, JS::HandleObject proxy,
JS::HandleId id, bool *bp);
bool (*getOwnEnumerablePropertyKeys)(JSContext *cx, JS::HandleObject proxy,
JS::AutoIdVector &props);
bool (*nativeCall)(JSContext *cx, JS::IsAcceptableThis test,
JS::NativeImpl impl, JS::CallArgs args);
bool (*hasInstance)(JSContext *cx, JS::HandleObject proxy,
JS::MutableHandleValue v, bool *bp);
bool (*objectClassIs)(JS::HandleObject obj, js::ESClass classValue,
JSContext *cx);
const char *(*className)(JSContext *cx, JS::HandleObject proxy);
JSString* (*fun_toString)(JSContext *cx, JS::HandleObject proxy,
bool isToString);
//bool (*regexp_toShared)(JSContext *cx, JS::HandleObject proxy, RegExpGuard *g);
bool (*boxedValue_unbox)(JSContext *cx, JS::HandleObject proxy,
JS::MutableHandleValue vp);
bool (*defaultValue)(JSContext *cx, JS::HandleObject obj, JSType hint, JS::MutableHandleValue vp);
void (*trace)(JSTracer *trc, JSObject *proxy);
void (*finalize)(JSFreeOp *fop, JSObject *proxy);
void (*objectMoved)(JSObject *proxy, const JSObject *old);
bool (*isCallable)(JSObject *obj);
bool (*isConstructor)(JSObject *obj);
// watch
// unwatch
// getElements
// weakmapKeyDelegate
// isScripted
};
static int HandlerFamily;
#define DEFER_TO_TRAP_OR_BASE_CLASS(_base) \
\
/* Standard internal methods. */ \
virtual JSObject* enumerate(JSContext *cx, \
JS::HandleObject proxy) const override \
{ \
return mTraps.enumerate \
? mTraps.enumerate(cx, proxy) \
: _base::enumerate(cx, proxy); \
} \
\
virtual bool has(JSContext* cx, JS::HandleObject proxy, \
JS::HandleId id, bool *bp) const override \
{ \
return mTraps.has \
? mTraps.has(cx, proxy, id, bp) \
: _base::has(cx, proxy, id, bp); \
} \
\
virtual bool get(JSContext* cx, JS::HandleObject proxy, \
JS::HandleValue receiver, \
JS::HandleId id, JS::MutableHandleValue vp) const override \
{ \
return mTraps.get \
? mTraps.get(cx, proxy, receiver, id, vp) \
: _base::get(cx, proxy, receiver, id, vp); \
} \
\
virtual bool set(JSContext* cx, JS::HandleObject proxy, \
JS::HandleId id, JS::HandleValue v, \
JS::HandleValue receiver, \
JS::ObjectOpResult &result) const override \
{ \
return mTraps.set \
? mTraps.set(cx, proxy, id, v, receiver, result) \
: _base::set(cx, proxy, id, v, receiver, result); \
} \
\
virtual bool call(JSContext* cx, JS::HandleObject proxy, \
const JS::CallArgs &args) const override \
{ \
return mTraps.call \
? mTraps.call(cx, proxy, args) \
: _base::call(cx, proxy, args); \
} \
\
virtual bool construct(JSContext* cx, JS::HandleObject proxy, \
const JS::CallArgs &args) const override \
{ \
return mTraps.construct \
? mTraps.construct(cx, proxy, args) \
: _base::construct(cx, proxy, args); \
} \
\
/* Spidermonkey extensions. */ \
virtual bool hasOwn(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, \
bool* bp) const override \
{ \
return mTraps.hasOwn \
? mTraps.hasOwn(cx, proxy, id, bp) \
: _base::hasOwn(cx, proxy, id, bp); \
} \
\
virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, \
JS::HandleObject proxy, \
JS::AutoIdVector &props) const override \
{ \
return mTraps.getOwnEnumerablePropertyKeys \
? mTraps.getOwnEnumerablePropertyKeys(cx, proxy, props) \
: _base::getOwnEnumerablePropertyKeys(cx, proxy, props); \
} \
\
virtual bool nativeCall(JSContext* cx, JS::IsAcceptableThis test, \
JS::NativeImpl impl, \
const JS::CallArgs& args) const override \
{ \
return mTraps.nativeCall \
? mTraps.nativeCall(cx, test, impl, args) \
: _base::nativeCall(cx, test, impl, args); \
} \
\
virtual bool hasInstance(JSContext* cx, JS::HandleObject proxy, \
JS::MutableHandleValue v, bool* bp) const override \
{ \
return mTraps.hasInstance \
? mTraps.hasInstance(cx, proxy, v, bp) \
: _base::hasInstance(cx, proxy, v, bp); \
} \
\
virtual const char *className(JSContext *cx, JS::HandleObject proxy) const override\
{ \
return mTraps.className \
? mTraps.className(cx, proxy) \
: _base::className(cx, proxy); \
} \
\
virtual JSString* fun_toString(JSContext* cx, JS::HandleObject proxy, \
bool isToString) const override \
{ \
return mTraps.fun_toString \
? mTraps.fun_toString(cx, proxy, isToString) \
: _base::fun_toString(cx, proxy, isToString); \
} \
\
virtual bool boxedValue_unbox(JSContext* cx, JS::HandleObject proxy, \
JS::MutableHandleValue vp) const override \
{ \
return mTraps.boxedValue_unbox \
? mTraps.boxedValue_unbox(cx, proxy, vp) \
: _base::boxedValue_unbox(cx, proxy, vp); \
} \
\
virtual void trace(JSTracer* trc, JSObject* proxy) const override \
{ \
mTraps.trace \
? mTraps.trace(trc, proxy) \
: _base::trace(trc, proxy); \
} \
\
virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override \
{ \
mTraps.finalize \
? mTraps.finalize(fop, proxy) \
: _base::finalize(fop, proxy); \
} \
\
virtual void objectMoved(JSObject* proxy, \
const JSObject *old) const override \
{ \
mTraps.objectMoved \
? mTraps.objectMoved(proxy, old) \
: _base::objectMoved(proxy, old); \
} \
\
virtual bool isCallable(JSObject* obj) const override \
{ \
return mTraps.isCallable \
? mTraps.isCallable(obj) \
: _base::isCallable(obj); \
} \
\
virtual bool isConstructor(JSObject* obj) const override \
{ \
return mTraps.isConstructor \
? mTraps.isConstructor(obj) \
: _base::isConstructor(obj); \
}
class WrapperProxyHandler : public js::Wrapper
{
ProxyTraps mTraps;
public:
WrapperProxyHandler(const ProxyTraps& aTraps)
: js::Wrapper(0), mTraps(aTraps) {}
virtual bool finalizeInBackground(const JS::Value& priv) const override
{
return false;
}
DEFER_TO_TRAP_OR_BASE_CLASS(js::Wrapper)
virtual bool getOwnPropertyDescriptor(JSContext *cx, JS::HandleObject proxy,
JS::HandleId id,
JS::MutableHandle<JS::PropertyDescriptor> desc) const override
{
return mTraps.getOwnPropertyDescriptor
? mTraps.getOwnPropertyDescriptor(cx, proxy, id, desc)
: js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
}
virtual bool defineProperty(JSContext *cx,
JS::HandleObject proxy, JS::HandleId id,
JS::Handle<JS::PropertyDescriptor> desc,
JS::ObjectOpResult &result) const override
{
return mTraps.defineProperty
? mTraps.defineProperty(cx, proxy, id, desc, result)
: js::Wrapper::defineProperty(cx, proxy, id, desc, result);
}
virtual bool ownPropertyKeys(JSContext *cx, JS::HandleObject proxy,
JS::AutoIdVector &props) const override
{
return mTraps.ownPropertyKeys
? mTraps.ownPropertyKeys(cx, proxy, props)
: js::Wrapper::ownPropertyKeys(cx, proxy, props);
}
virtual bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
JS::ObjectOpResult &result) const override
{
return mTraps.delete_
? mTraps.delete_(cx, proxy, id, result)
: js::Wrapper::delete_(cx, proxy, id, result);
}
virtual bool preventExtensions(JSContext *cx, JS::HandleObject proxy,
JS::ObjectOpResult &result) const override
{
return mTraps.preventExtensions
? mTraps.preventExtensions(cx, proxy, result)
: js::Wrapper::preventExtensions(cx, proxy, result);
}
virtual bool isExtensible(JSContext *cx, JS::HandleObject proxy,
bool *succeeded) const override
{
return mTraps.isExtensible
? mTraps.isExtensible(cx, proxy, succeeded)
: js::Wrapper::isExtensible(cx, proxy, succeeded);
}
virtual bool getPropertyDescriptor(JSContext *cx, JS::HandleObject proxy,
JS::HandleId id,
JS::MutableHandle<JS::PropertyDescriptor> desc) const override
{
return mTraps.getPropertyDescriptor
? mTraps.getPropertyDescriptor(cx, proxy, id, desc)
: js::Wrapper::getPropertyDescriptor(cx, proxy, id, desc);
}
};
class RustJSPrincipal : public JSPrincipals
{
const void* origin; //box with origin in it
void (*destroyCallback)(JSPrincipals *principal);
bool (*writeCallback)(JSContext* cx, JSStructuredCloneWriter* writer);
public:
RustJSPrincipal(const void* origin,
void (*destroy)(JSPrincipals *principal),
bool (*write)(JSContext* cx, JSStructuredCloneWriter* writer))
: JSPrincipals() {
this->origin = origin;
this->destroyCallback = destroy;
this->writeCallback = write;
}
virtual const void* getOrigin() {
return origin;
}
virtual void destroy() {
if(this->destroyCallback)
this->destroyCallback(this);
}
bool write(JSContext* cx, JSStructuredCloneWriter* writer) {
return this->writeCallback
? this->writeCallback(cx, writer)
: false;
}
};
class ForwardingProxyHandler : public js::BaseProxyHandler
{
ProxyTraps mTraps;
const void* mExtra;
public:
ForwardingProxyHandler(const ProxyTraps& aTraps, const void* aExtra)
: js::BaseProxyHandler(&HandlerFamily), mTraps(aTraps), mExtra(aExtra) {}
const void* getExtra() const {
return mExtra;
}
virtual bool finalizeInBackground(const JS::Value& priv) const override
{
return false;
}
DEFER_TO_TRAP_OR_BASE_CLASS(BaseProxyHandler)
virtual bool getOwnPropertyDescriptor(JSContext *cx, JS::HandleObject proxy,
JS::HandleId id,
JS::MutableHandle<JS::PropertyDescriptor> desc) const override
{
return mTraps.getOwnPropertyDescriptor(cx, proxy, id, desc);
}
virtual bool defineProperty(JSContext *cx,
JS::HandleObject proxy, JS::HandleId id,
JS::Handle<JS::PropertyDescriptor> desc,
JS::ObjectOpResult &result) const override
{
return mTraps.defineProperty(cx, proxy, id, desc, result);
}
virtual bool ownPropertyKeys(JSContext *cx, JS::HandleObject proxy,
JS::AutoIdVector &props) const override
{
return mTraps.ownPropertyKeys(cx, proxy, props);
}
virtual bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
JS::ObjectOpResult &result) const override
{
return mTraps.delete_(cx, proxy, id, result);
}
virtual bool getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject proxy,
bool* isOrdinary,
JS::MutableHandleObject protop) const override
{
return mTraps.getPrototypeIfOrdinary(cx, proxy, isOrdinary, protop);
}
virtual bool preventExtensions(JSContext *cx, JS::HandleObject proxy,
JS::ObjectOpResult &result) const override
{
return mTraps.preventExtensions(cx, proxy, result);
}
virtual bool isExtensible(JSContext *cx, JS::HandleObject proxy,
bool *succeeded) const override
{
return mTraps.isExtensible(cx, proxy, succeeded);
}
virtual bool getPropertyDescriptor(JSContext *cx, JS::HandleObject proxy,
JS::HandleId id,
JS::MutableHandle<JS::PropertyDescriptor> desc) const override
{
return mTraps.getPropertyDescriptor(cx, proxy, id, desc);
}
};
extern "C" {
JSPrincipals*
CreateRustJSPrincipal(const void* origin,
void (*destroy)(JSPrincipals *principal),
bool (*write)(JSContext* cx, JSStructuredCloneWriter *writer)){
return new RustJSPrincipal(origin, destroy, write);
}
const void*
GetPrincipalOrigin(JSPrincipals* principal) {
return static_cast<RustJSPrincipal*>(principal)->getOrigin();
}
bool
InvokeGetOwnPropertyDescriptor(
const void *handler,
JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
JS::MutableHandle<JS::PropertyDescriptor> desc)
{
return static_cast<const ForwardingProxyHandler*>(handler)->
getOwnPropertyDescriptor(cx, proxy, id, desc);
}
bool
InvokeHasOwn(
const void *handler,
JSContext *cx, JS::HandleObject proxy,
JS::HandleId id, bool *bp)
{
return static_cast<const js::BaseProxyHandler*>(handler)->
hasOwn(cx, proxy, id, bp);
}
JS::Value
RUST_JS_NumberValue(double d)
{
return JS_NumberValue(d);
}
const JSJitInfo*
RUST_FUNCTION_VALUE_TO_JITINFO(JS::Value v)
{
return FUNCTION_VALUE_TO_JITINFO(v);
}
JS::CallArgs
CreateCallArgsFromVp(unsigned argc, JS::Value* vp)
{
return JS::CallArgsFromVp(argc, vp);
}
bool
CallJitGetterOp(const JSJitInfo* info, JSContext* cx,
JS::HandleObject thisObj, void* specializedThis,
unsigned argc, JS::Value* vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
return info->getter(cx, thisObj, specializedThis, JSJitGetterCallArgs(args));
}
bool
CallJitSetterOp(const JSJitInfo* info, JSContext* cx,
JS::HandleObject thisObj, void* specializedThis,
unsigned argc, JS::Value* vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
return info->setter(cx, thisObj, specializedThis, JSJitSetterCallArgs(args));
}
bool
CallJitMethodOp(const JSJitInfo* info, JSContext* cx,
JS::HandleObject thisObj, void* specializedThis,
uint32_t argc, JS::Value* vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
return info->method(cx, thisObj, specializedThis, JSJitMethodCallArgs(args));
}
const void*
CreateProxyHandler(const ProxyTraps* aTraps, const void* aExtra)
{
return new ForwardingProxyHandler(*aTraps, aExtra);
}
const void*
CreateWrapperProxyHandler(const ProxyTraps* aTraps)
{
return new WrapperProxyHandler(*aTraps);
}
const void*
GetCrossCompartmentWrapper()
{
return &js::CrossCompartmentWrapper::singleton;
}
const void*
GetSecurityWrapper()
{
return &js::CrossCompartmentSecurityWrapper::singleton;
}
JS::ReadOnlyCompileOptions*
NewCompileOptions(JSContext* aCx, const char* aFile, unsigned aLine)
{
JS::OwningCompileOptions *opts = new JS::OwningCompileOptions(aCx);
opts->setFileAndLine(aCx, aFile, aLine);
opts->setVersion(JSVERSION_DEFAULT);
return opts;
}
void
DeleteCompileOptions(JS::ReadOnlyCompileOptions *aOpts)
{
delete static_cast<JS::OwningCompileOptions *>(aOpts);
}
JSObject*
NewProxyObject(JSContext* aCx, const void* aHandler, JS::HandleValue aPriv,
JSObject* proto, JSObject* parent, JSObject* call,
JSObject* construct)
{
js::ProxyOptions options;
return js::NewProxyObject(aCx, (js::BaseProxyHandler*)aHandler, aPriv, proto,
options);
}
JSObject*
WrapperNew(JSContext* aCx, JS::HandleObject aObj, const void* aHandler,
const JSClass* aClass, bool aSingleton)
{
js::WrapperOptions options;
if (aClass) {
options.setClass(js::Valueify(aClass));
}
options.setSingleton(aSingleton);
return js::Wrapper::New(aCx, aObj, (const js::Wrapper*)aHandler, options);
}
void WindowProxyObjectMoved(JSObject*, const JSObject*)
{
abort();
}
static const js::ClassExtension WindowProxyClassExtension = PROXY_MAKE_EXT(
WindowProxyObjectMoved
);
const js::Class WindowProxyClass = PROXY_CLASS_WITH_EXT(
"Proxy",
JSCLASS_HAS_RESERVED_SLOTS(1), /* additional class flags */
&WindowProxyClassExtension);
const js::Class*
GetWindowProxyClass()
{
return &WindowProxyClass;
}
JS::Value
GetProxyReservedSlot(JSObject* obj, uint32_t slot)
{
return js::GetProxyReservedSlot(obj, slot);
}
void
SetProxyReservedSlot(JSObject* obj, uint32_t slot, const JS::Value* val)
{
js::SetProxyReservedSlot(obj, slot, *val);
}
JSObject*
NewWindowProxy(JSContext* aCx, JS::HandleObject aObj, const void* aHandler)
{
return WrapperNew(aCx, aObj, aHandler, Jsvalify(&WindowProxyClass), true);
}
JS::Value
GetProxyPrivate(JSObject* obj)
{
return js::GetProxyPrivate(obj);
}
void
SetProxyPrivate(JSObject* obj, const JS::Value* expando)
{
js::SetProxyPrivate(obj, *expando);
}
bool
RUST_JSID_IS_INT(JS::HandleId id)
{
return JSID_IS_INT(id);
}
jsid
int_to_jsid(int32_t i)
{
return INT_TO_JSID(i);
}
int32_t
RUST_JSID_TO_INT(JS::HandleId id)
{
return JSID_TO_INT(id);
}
bool
RUST_JSID_IS_STRING(JS::HandleId id)
{
return JSID_IS_STRING(id);
}
JSString*
RUST_JSID_TO_STRING(JS::HandleId id)
{
return JSID_TO_STRING(id);
}
jsid
RUST_SYMBOL_TO_JSID(JS::Symbol* sym)
{
return SYMBOL_TO_JSID(sym);
}
void
RUST_SET_JITINFO(JSFunction* func, const JSJitInfo* info) {
SET_JITINFO(func, info);
}
jsid
RUST_INTERNED_STRING_TO_JSID(JSContext* cx, JSString* str) {
return INTERNED_STRING_TO_JSID(cx, str);
}
const JSErrorFormatString*
RUST_js_GetErrorMessage(void* userRef, uint32_t errorNumber)
{
return js::GetErrorMessage(userRef, errorNumber);
}
bool
IsProxyHandlerFamily(JSObject* obj)
{
auto family = js::GetProxyHandler(obj)->family();
return family == &HandlerFamily;
}
const void*
GetProxyHandlerFamily()
{
return &HandlerFamily;
}
const void*
GetProxyHandlerExtra(JSObject* obj)
{
const js::BaseProxyHandler* handler = js::GetProxyHandler(obj);
assert(handler->family() == &HandlerFamily);
return static_cast<const ForwardingProxyHandler*>(handler)->getExtra();
}
const void*
GetProxyHandler(JSObject* obj)
{
const js::BaseProxyHandler* handler = js::GetProxyHandler(obj);
assert(handler->family() == &HandlerFamily);
return handler;
}
void
ReportError(JSContext* aCx, const char* aError)
{
#ifdef DEBUG
for (const char* p = aError; *p; ++p) {
assert(*p != '%');
}
#endif
JS_ReportErrorASCII(aCx, "%s", aError);
}
bool
IsWrapper(JSObject* obj)
{
return js::IsWrapper(obj);
}
JSObject*
UnwrapObject(JSObject* obj, bool stopAtOuter)
{
return js::CheckedUnwrap(obj, stopAtOuter);
}
JSObject*
UncheckedUnwrapObject(JSObject* obj, bool stopAtOuter)
{
return js::UncheckedUnwrap(obj, stopAtOuter);
}
JS::AutoIdVector*
CreateAutoIdVector(JSContext* cx)
{
return new JS::AutoIdVector(cx);
}
bool
AppendToAutoIdVector(JS::AutoIdVector* v, jsid id)
{
return v->append(id);
}
const jsid*
SliceAutoIdVector(const JS::AutoIdVector* v, size_t* length)
{
*length = v->length();
return v->begin();
}
void
DestroyAutoIdVector(JS::AutoIdVector* v)
{
delete v;
}
JS::AutoObjectVector*
CreateAutoObjectVector(JSContext* aCx)
{
JS::AutoObjectVector* vec = new JS::AutoObjectVector(aCx);
return vec;
}
bool
AppendToAutoObjectVector(JS::AutoObjectVector* v, JSObject* obj)
{
return v->append(obj);
}
void
DeleteAutoObjectVector(JS::AutoObjectVector* v)
{
delete v;
}
#if defined(__linux__)
#include <malloc.h>
#elif defined(__APPLE__)
#include <malloc/malloc.h>
#elif defined(__MINGW32__) || defined(__MINGW64__)
// nothing needed here
#elif defined(_MSC_VER)
// nothing needed here
#else
#error "unsupported platform"
#endif
// SpiderMonkey-in-Rust currently uses system malloc, not jemalloc.
static size_t MallocSizeOf(const void* aPtr)
{
#if defined(__linux__)
return malloc_usable_size((void*)aPtr);
#elif defined(__APPLE__)
return malloc_size((void*)aPtr);
#elif defined(__MINGW32__) || defined(__MINGW64__)
return _msize((void*)aPtr);
#elif defined(_MSC_VER)
return _msize((void*)aPtr);
#else
#error "unsupported platform"
#endif
}
bool
CollectServoSizes(JSContext* cx, JS::ServoSizes *sizes)
{
mozilla::PodZero(sizes);
return JS::AddServoSizeOf(cx, MallocSizeOf,
/* ObjectPrivateVisitor = */ nullptr, sizes);
}
void
CallValueTracer(JSTracer* trc, JS::Heap<JS::Value>* valuep, const char* name)
{
JS::TraceEdge(trc, valuep, name);
}
void
CallIdTracer(JSTracer* trc, JS::Heap<jsid>* idp, const char* name)
{
JS::TraceEdge(trc, idp, name);
}
void
CallObjectTracer(JSTracer* trc, JS::Heap<JSObject*>* objp, const char* name)
{
JS::TraceEdge(trc, objp, name);
}
void
CallStringTracer(JSTracer* trc, JS::Heap<JSString*>* strp, const char* name)
{
JS::TraceEdge(trc, strp, name);
}
void
CallScriptTracer(JSTracer* trc, JS::Heap<JSScript*>* scriptp, const char* name)
{
JS::TraceEdge(trc, scriptp, name);
}
void
CallFunctionTracer(JSTracer* trc, JS::Heap<JSFunction*>* funp, const char* name)
{
JS::TraceEdge(trc, funp, name);
}
void
CallUnbarrieredObjectTracer(JSTracer* trc, JSObject** objp, const char* name)
{
js::UnsafeTraceManuallyBarrieredEdge(trc, objp, name);
}
bool
IsDebugBuild()
{
#ifdef JS_DEBUG
return true;
#else
return false;
#endif
}
#define JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Type, type) \
void \
Get ## Type ## ArrayLengthAndData(JSObject* obj, uint32_t* length, \
bool* isSharedMemory, type** data) \
{ \
js::Get ## Type ## ArrayLengthAndData(obj, length, isSharedMemory, \
data); \
}
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int8, int8_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8, uint8_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8Clamped, uint8_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int16, int16_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint16, uint16_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int32, int32_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint32, uint32_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float32, float)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float64, double)
#undef JS_DEFINE_DATA_AND_LENGTH_ACCESSOR
JSAutoStructuredCloneBuffer*
NewJSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope,
const JSStructuredCloneCallbacks* callbacks)
{
return js_new<JSAutoStructuredCloneBuffer>(scope, callbacks, nullptr);
}
JSStructuredCloneData*
DeleteJSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer* buf)
{
js_delete(buf);
}
size_t
GetLengthOfJSStructuredCloneData(JSStructuredCloneData* data)
{
assert(data != nullptr);
size_t len = 0;
auto iter = data->Iter();
while (!iter.Done()) {
size_t len_of_this_segment = iter.RemainingInSegment();
len += len_of_this_segment;
iter.Advance(*data, len_of_this_segment);
}
return len;
}
void
CopyJSStructuredCloneData(JSStructuredCloneData* src, uint8_t* dest)
{
assert(src != nullptr);
assert(dest != nullptr);
size_t bytes_copied = 0;
auto iter = src->Iter();
while (!iter.Done()) {
size_t len_of_this_segment = iter.RemainingInSegment();
memcpy(dest + bytes_copied, iter.Data(), len_of_this_segment);
bytes_copied += len_of_this_segment;
iter.Advance(*src, len_of_this_segment);
}
}
bool
WriteBytesToJSStructuredCloneData(const uint8_t* src, size_t len, JSStructuredCloneData* dest)
{
assert(src != nullptr);
assert(dest != nullptr);
return dest->WriteBytes(reinterpret_cast<const char*>(src), len);
}
} // extern "C"

549
js/rust/src/jsval.rs Normal file
View File

@ -0,0 +1,549 @@
/* 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/. */
use jsapi::root::*;
use libc::c_void;
use std::mem;
#[cfg(target_pointer_width = "64")]
const JSVAL_TAG_SHIFT: usize = 47;
#[cfg(target_pointer_width = "64")]
const JSVAL_TAG_MAX_DOUBLE: u32 = 0x1FFF0u32;
#[cfg(target_pointer_width = "32")]
const JSVAL_TAG_CLEAR: u32 = 0xFFFFFF80;
#[cfg(target_pointer_width = "64")]
#[repr(u32)]
#[allow(dead_code)]
#[derive(Clone, Copy, Debug)]
enum ValueTag {
INT32 = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_INT32 as u32),
UNDEFINED = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_UNDEFINED as u32),
STRING = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_STRING as u32),
SYMBOL = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_SYMBOL as u32),
BOOLEAN = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_BOOLEAN as u32),
MAGIC = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_MAGIC as u32),
NULL = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_NULL as u32),
OBJECT = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_OBJECT as u32),
}
#[cfg(target_pointer_width = "32")]
#[repr(u32)]
#[allow(dead_code)]
#[derive(Clone, Copy, Debug)]
enum ValueTag {
PRIVATE = 0,
INT32 = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_INT32 as u32),
UNDEFINED = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_UNDEFINED as u32),
STRING = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_STRING as u32),
SYMBOL = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_SYMBOL as u32),
BOOLEAN = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_BOOLEAN as u32),
MAGIC = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_MAGIC as u32),
NULL = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_NULL as u32),
OBJECT = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_OBJECT as u32),
}
#[cfg(target_pointer_width = "64")]
#[repr(u64)]
#[allow(dead_code)]
#[derive(Clone, Copy, Debug)]
enum ValueShiftedTag {
MAX_DOUBLE = (((JSVAL_TAG_MAX_DOUBLE as u64) << JSVAL_TAG_SHIFT) | 0xFFFFFFFFu64),
INT32 = ((ValueTag::INT32 as u64) << JSVAL_TAG_SHIFT),
UNDEFINED = ((ValueTag::UNDEFINED as u64) << JSVAL_TAG_SHIFT),
STRING = ((ValueTag::STRING as u64) << JSVAL_TAG_SHIFT),
SYMBOL = ((ValueTag::SYMBOL as u64) << JSVAL_TAG_SHIFT),
BOOLEAN = ((ValueTag::BOOLEAN as u64) << JSVAL_TAG_SHIFT),
MAGIC = ((ValueTag::MAGIC as u64) << JSVAL_TAG_SHIFT),
NULL = ((ValueTag::NULL as u64) << JSVAL_TAG_SHIFT),
OBJECT = ((ValueTag::OBJECT as u64) << JSVAL_TAG_SHIFT),
}
#[cfg(target_pointer_width = "64")]
const JSVAL_PAYLOAD_MASK: u64 = 0x00007FFFFFFFFFFF;
#[inline(always)]
fn AsJSVal(val: u64) -> JS::Value {
JS::Value {
data: JS::Value_layout {
asBits: val,
}
}
}
#[cfg(target_pointer_width = "64")]
#[inline(always)]
fn BuildJSVal(tag: ValueTag, payload: u64) -> JS::Value {
AsJSVal(((tag as u32 as u64) << JSVAL_TAG_SHIFT) | payload)
}
#[cfg(target_pointer_width = "32")]
#[inline(always)]
fn BuildJSVal(tag: ValueTag, payload: u64) -> JS::Value {
AsJSVal(((tag as u32 as u64) << 32) | payload)
}
#[inline(always)]
pub fn NullValue() -> JS::Value {
BuildJSVal(ValueTag::NULL, 0)
}
#[inline(always)]
pub fn UndefinedValue() -> JS::Value {
BuildJSVal(ValueTag::UNDEFINED, 0)
}
#[inline(always)]
pub fn Int32Value(i: i32) -> JS::Value {
BuildJSVal(ValueTag::INT32, i as u32 as u64)
}
#[cfg(target_pointer_width = "64")]
#[inline(always)]
pub fn DoubleValue(f: f64) -> JS::Value {
let bits: u64 = unsafe { mem::transmute(f) };
assert!(bits <= ValueShiftedTag::MAX_DOUBLE as u64);
AsJSVal(bits)
}
#[cfg(target_pointer_width = "32")]
#[inline(always)]
pub fn DoubleValue(f: f64) -> JS::Value {
let bits: u64 = unsafe { mem::transmute(f) };
let val = AsJSVal(bits);
assert!(val.is_double());
val
}
#[inline(always)]
pub fn UInt32Value(ui: u32) -> JS::Value {
if ui > 0x7fffffff {
DoubleValue(ui as f64)
} else {
Int32Value(ui as i32)
}
}
#[cfg(target_pointer_width = "64")]
#[inline(always)]
pub fn StringValue(s: &JSString) -> JS::Value {
let bits = s as *const JSString as usize as u64;
assert!((bits >> JSVAL_TAG_SHIFT) == 0);
BuildJSVal(ValueTag::STRING, bits)
}
#[cfg(target_pointer_width = "32")]
#[inline(always)]
pub fn StringValue(s: &JSString) -> JS::Value {
let bits = s as *const JSString as usize as u64;
BuildJSVal(ValueTag::STRING, bits)
}
#[inline(always)]
pub fn BooleanValue(b: bool) -> JS::Value {
BuildJSVal(ValueTag::BOOLEAN, b as u64)
}
#[cfg(target_pointer_width = "64")]
#[inline(always)]
pub fn ObjectValue(o: *mut JSObject) -> JS::Value {
let bits = o as usize as u64;
assert!((bits >> JSVAL_TAG_SHIFT) == 0);
BuildJSVal(ValueTag::OBJECT, bits)
}
#[cfg(target_pointer_width = "32")]
#[inline(always)]
pub fn ObjectValue(o: *mut JSObject) -> JS::Value {
let bits = o as usize as u64;
BuildJSVal(ValueTag::OBJECT, bits)
}
#[inline(always)]
pub fn ObjectOrNullValue(o: *mut JSObject) -> JS::Value {
if o.is_null() {
NullValue()
} else {
ObjectValue(o)
}
}
#[cfg(target_pointer_width = "64")]
#[inline(always)]
pub fn PrivateValue(o: *const c_void) -> JS::Value {
let ptrBits = o as usize as u64;
assert!((ptrBits & 1) == 0);
AsJSVal(ptrBits >> 1)
}
#[cfg(target_pointer_width = "32")]
#[inline(always)]
pub fn PrivateValue(o: *const c_void) -> JS::Value {
let ptrBits = o as usize as u64;
assert!((ptrBits & 1) == 0);
BuildJSVal(ValueTag::PRIVATE, ptrBits)
}
impl JS::Value {
#[inline(always)]
unsafe fn asBits(&self) -> u64 {
self.data.asBits
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn is_undefined(&self) -> bool {
unsafe {
self.asBits() == ValueShiftedTag::UNDEFINED as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn is_undefined(&self) -> bool {
unsafe {
(self.asBits() >> 32) == ValueTag::UNDEFINED as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn is_null(&self) -> bool {
unsafe {
self.asBits() == ValueShiftedTag::NULL as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn is_null(&self) -> bool {
unsafe {
(self.asBits() >> 32) == ValueTag::NULL as u64
}
}
#[inline(always)]
pub fn is_null_or_undefined(&self) -> bool {
self.is_null() || self.is_undefined()
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn is_boolean(&self) -> bool {
unsafe {
(self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::BOOLEAN as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn is_boolean(&self) -> bool {
unsafe {
(self.asBits() >> 32) == ValueTag::BOOLEAN as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn is_int32(&self) -> bool {
unsafe {
(self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::INT32 as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn is_int32(&self) -> bool {
unsafe {
(self.asBits() >> 32) == ValueTag::INT32 as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn is_double(&self) -> bool {
unsafe {
self.asBits() <= ValueShiftedTag::MAX_DOUBLE as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn is_double(&self) -> bool {
unsafe {
(self.asBits() >> 32) <= JSVAL_TAG_CLEAR as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn is_number(&self) -> bool {
const JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET: u64 = ValueShiftedTag::UNDEFINED as u64;
unsafe {
self.asBits() < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn is_number(&self) -> bool {
const JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET: u64 = ValueTag::INT32 as u64;
unsafe {
(self.asBits() >> 32) <= JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET
}
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn is_primitive(&self) -> bool {
const JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET: u64 = ValueShiftedTag::OBJECT as u64;
unsafe {
self.asBits() < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn is_primitive(&self) -> bool {
const JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET: u64 = ValueTag::OBJECT as u64;
unsafe {
(self.asBits() >> 32) < JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET
}
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn is_string(&self) -> bool {
unsafe {
(self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::STRING as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn is_string(&self) -> bool {
unsafe {
(self.asBits() >> 32) == ValueTag::STRING as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn is_object(&self) -> bool {
unsafe {
assert!((self.asBits() >> JSVAL_TAG_SHIFT) <= ValueTag::OBJECT as u64);
self.asBits() >= ValueShiftedTag::OBJECT as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn is_object(&self) -> bool {
unsafe {
(self.asBits() >> 32) == ValueTag::OBJECT as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn is_symbol(&self) -> bool {
unsafe {
self.asBits() == ValueShiftedTag::SYMBOL as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn is_symbol(&self) -> bool {
unsafe {
(self.asBits() >> 32) == ValueTag::SYMBOL as u64
}
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn to_boolean(&self) -> bool {
assert!(self.is_boolean());
unsafe {
(self.asBits() & JSVAL_PAYLOAD_MASK) != 0
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn to_boolean(&self) -> bool {
assert!(self.is_boolean());
unsafe {
(self.asBits() & 0x00000000FFFFFFFF) != 0
}
}
#[inline(always)]
pub fn to_int32(&self) -> i32 {
assert!(self.is_int32());
unsafe {
(self.asBits() & 0x00000000FFFFFFFF) as i32
}
}
#[inline(always)]
pub fn to_double(&self) -> f64 {
assert!(self.is_double());
unsafe { mem::transmute(self.asBits()) }
}
#[inline(always)]
pub fn to_number(&self) -> f64 {
assert!(self.is_number());
if self.is_double() {
self.to_double()
} else {
self.to_int32() as f64
}
}
#[inline(always)]
pub fn to_object(&self) -> *mut JSObject {
assert!(self.is_object());
self.to_object_or_null()
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn to_string(&self) -> *mut JSString {
assert!(self.is_string());
unsafe {
let ptrBits = self.asBits() & JSVAL_PAYLOAD_MASK;
ptrBits as usize as *mut JSString
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn to_string(&self) -> *mut JSString {
assert!(self.is_string());
unsafe {
let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
ptrBits as *mut JSString
}
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn is_object_or_null(&self) -> bool {
const JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET: u64 = ValueShiftedTag::NULL as u64;
unsafe {
assert!((self.asBits() >> JSVAL_TAG_SHIFT) <= ValueTag::OBJECT as u64);
self.asBits() >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn is_object_or_null(&self) -> bool {
const JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET: u64 = ValueTag::NULL as u64;
unsafe {
assert!((self.asBits() >> 32) <= ValueTag::OBJECT as u64);
(self.asBits() >> 32) >= JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET
}
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn to_object_or_null(&self) -> *mut JSObject {
assert!(self.is_object_or_null());
unsafe {
let ptrBits = self.asBits() & JSVAL_PAYLOAD_MASK;
assert!((ptrBits & 0x7) == 0);
ptrBits as usize as *mut JSObject
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn to_object_or_null(&self) -> *mut JSObject {
assert!(self.is_object_or_null());
unsafe {
let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
ptrBits as *mut JSObject
}
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn to_private(&self) -> *const c_void {
assert!(self.is_double());
unsafe {
assert!((self.asBits() & 0x8000000000000000u64) == 0);
(self.asBits() << 1) as usize as *const c_void
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn to_private(&self) -> *const c_void {
unsafe {
let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
ptrBits as *const c_void
}
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn is_gcthing(&self) -> bool {
const JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET: u64 = ValueShiftedTag::STRING as u64;
unsafe {
self.asBits() >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn is_gcthing(&self) -> bool {
const JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET: u64 = ValueTag::STRING as u64;
unsafe {
(self.asBits() >> 32) >= JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET
}
}
#[inline(always)]
#[cfg(target_pointer_width = "64")]
pub fn to_gcthing(&self) -> *mut c_void {
assert!(self.is_gcthing());
unsafe {
let ptrBits = self.asBits() & JSVAL_PAYLOAD_MASK;
assert!((ptrBits & 0x7) == 0);
ptrBits as *mut c_void
}
}
#[inline(always)]
#[cfg(target_pointer_width = "32")]
pub fn to_gcthing(&self) -> *mut c_void {
assert!(self.is_gcthing());
unsafe {
let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
ptrBits as *mut c_void
}
}
#[inline(always)]
pub fn is_markable(&self) -> bool {
self.is_gcthing() && !self.is_null()
}
#[inline(always)]
pub fn trace_kind(&self) -> JS::TraceKind {
assert!(self.is_markable());
if self.is_object() {
JS::TraceKind::Object
} else {
JS::TraceKind::String
}
}
}

54
js/rust/src/lib.rs Normal file
View File

@ -0,0 +1,54 @@
/* 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/. */
#![crate_name = "js"]
#![crate_type = "rlib"]
#![cfg_attr(feature = "nonzero", feature(nonzero))]
#![allow(non_upper_case_globals, non_camel_case_types, non_snake_case, improper_ctypes)]
#[cfg(feature = "nonzero")]
extern crate core;
#[macro_use]
extern crate heapsize;
#[macro_use]
extern crate lazy_static;
extern crate libc;
#[macro_use]
extern crate log;
#[allow(unused_extern_crates)]
extern crate mozjs_sys;
extern crate num_traits;
#[macro_use]
pub mod rust;
pub mod ac;
pub mod conversions;
pub mod error;
pub mod glue;
pub mod heap;
pub mod jsval;
pub mod panic;
pub mod sc;
pub mod typedarray;
pub mod jsapi;
use self::jsapi::root::*;
#[inline(always)]
pub unsafe fn JS_ARGV(_cx: *mut JSContext, vp: *mut JS::Value) -> *mut JS::Value {
vp.offset(2)
}
known_heap_size!(0, JS::Value);
impl JS::ObjectOpResult {
/// Set this ObjectOpResult to true and return true.
pub fn succeed(&mut self) -> bool {
self.code_ = JS::ObjectOpResult_SpecialCodes::OkCode as usize;
true
}
}

33
js/rust/src/panic.rs Normal file
View File

@ -0,0 +1,33 @@
/* 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/. */
use std::any::Any;
use std::cell::RefCell;
use std::panic::{UnwindSafe, catch_unwind, resume_unwind};
thread_local!(static PANIC_RESULT: RefCell<Option<Box<Any + Send>>> = RefCell::new(None));
/// If there is a pending panic, resume unwinding.
pub fn maybe_resume_unwind() {
if let Some(error) = PANIC_RESULT.with(|result| result.borrow_mut().take()) {
resume_unwind(error);
}
}
/// Generic wrapper for JS engine callbacks panic-catching
pub fn wrap_panic<F, R>(function: F, generic_return_type: R) -> R
where F: FnOnce() -> R + UnwindSafe
{
let result = catch_unwind(function);
match result {
Ok(result) => result,
Err(error) => {
PANIC_RESULT.with(|result| {
assert!(result.borrow().is_none());
*result.borrow_mut() = Some(error);
});
generic_return_type
}
}
}

1120
js/rust/src/rust.rs Normal file

File diff suppressed because it is too large Load Diff

102
js/rust/src/sc.rs Normal file
View File

@ -0,0 +1,102 @@
/* 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/. */
//! Nicer, Rust-y APIs for structured cloning.
use glue;
use jsapi;
use rust::Runtime;
use std::ptr;
/// An RAII owned buffer for structured cloning into and out of.
pub struct StructuredCloneBuffer {
raw: *mut jsapi::JSAutoStructuredCloneBuffer,
}
impl StructuredCloneBuffer {
/// Construct a new `StructuredCloneBuffer`.
///
/// # Panics
///
/// Panics if the underlying JSAPI calls fail.
pub fn new(scope: jsapi::JS::StructuredCloneScope,
callbacks: &jsapi::JSStructuredCloneCallbacks)
-> StructuredCloneBuffer {
let raw = unsafe {
glue::NewJSAutoStructuredCloneBuffer(scope, callbacks)
};
assert!(!raw.is_null());
StructuredCloneBuffer {
raw: raw,
}
}
/// Get the raw `*mut JSStructuredCloneData` owned by this buffer.
pub fn data(&self) -> *mut jsapi::JSStructuredCloneData {
unsafe {
&mut (*self.raw).data_
}
}
/// Copy this buffer's data into a vec.
pub fn copy_to_vec(&self) -> Vec<u8> {
let len = unsafe {
glue::GetLengthOfJSStructuredCloneData(self.data())
};
let mut vec = Vec::with_capacity(len);
unsafe {
glue::CopyJSStructuredCloneData(self.data(), vec.as_mut_ptr());
}
vec
}
/// Read a JS value out of this buffer.
pub fn read(&mut self,
vp: jsapi::JS::MutableHandleValue,
callbacks: &jsapi::JSStructuredCloneCallbacks)
-> Result<(), ()> {
if unsafe {
(*self.raw).read(Runtime::get(), vp, callbacks, ptr::null_mut())
} {
Ok(())
} else {
Err(())
}
}
/// Write a JS value into this buffer.
pub fn write(&mut self,
v: jsapi::JS::HandleValue,
callbacks: &jsapi::JSStructuredCloneCallbacks)
-> Result<(), ()> {
if unsafe {
(*self.raw).write(Runtime::get(), v, callbacks, ptr::null_mut())
} {
Ok(())
} else {
Err(())
}
}
/// Copy the given slice into this buffer.
pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), ()> {
let len = bytes.len();
let src = bytes.as_ptr();
if unsafe {
glue::WriteBytesToJSStructuredCloneData(src, len, self.data())
} {
Ok(())
} else {
Err(())
}
}
}
impl Drop for StructuredCloneBuffer {
fn drop(&mut self) {
unsafe {
glue::DeleteJSAutoStructuredCloneBuffer(self.raw);
}
}
}

301
js/rust/src/typedarray.rs Normal file
View File

@ -0,0 +1,301 @@
/* 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/. */
//! High-level, safe bindings for JS typed array APIs. Allows creating new
//! typed arrays or wrapping existing JS reflectors, and prevents reinterpreting
//! existing buffers as different types except in well-defined cases.
use glue::GetFloat32ArrayLengthAndData;
use glue::GetFloat64ArrayLengthAndData;
use glue::GetInt16ArrayLengthAndData;
use glue::GetInt32ArrayLengthAndData;
use glue::GetInt8ArrayLengthAndData;
use glue::GetUint16ArrayLengthAndData;
use glue::GetUint32ArrayLengthAndData;
use glue::GetUint8ArrayLengthAndData;
use glue::GetUint8ClampedArrayLengthAndData;
use jsapi::*;
use jsapi::js::*;
use jsapi::JS::*;
use rust::RootedGuard;
use std::ptr;
use std::slice;
pub enum CreateWith<'a, T: 'a> {
Length(u32),
Slice(&'a [T]),
}
/// A rooted typed array.
pub struct TypedArray<'a, T: 'a + TypedArrayElement> {
object: RootedGuard<'a, *mut JSObject>,
computed: Option<(*mut T::Element, u32)>,
}
impl<'a, T: TypedArrayElement> TypedArray<'a, T> {
/// Create a typed array representation that wraps an existing JS reflector.
/// This operation will fail if attempted on a JS object that does not match
/// the expected typed array details.
pub fn from(cx: *mut JSContext,
root: &'a mut Rooted<*mut JSObject>,
object: *mut JSObject)
-> Result<Self, ()> {
if object.is_null() {
return Err(());
}
unsafe {
let mut guard = RootedGuard::new(cx, root, object);
let unwrapped = T::unwrap_array(*guard);
if unwrapped.is_null() {
return Err(());
}
*guard = unwrapped;
Ok(TypedArray {
object: guard,
computed: None,
})
}
}
fn data(&mut self) -> (*mut T::Element, u32) {
if let Some(data) = self.computed {
return data;
}
let data = unsafe { T::length_and_data(*self.object) };
self.computed = Some(data);
data
}
/// # Unsafety
///
/// The returned slice can be invalidated if the underlying typed array
/// is neutered.
pub unsafe fn as_slice(&mut self) -> &[T::Element] {
let (pointer, length) = self.data();
slice::from_raw_parts(pointer as *const T::Element, length as usize)
}
/// # Unsafety
///
/// The returned slice can be invalidated if the underlying typed array
/// is neutered.
///
/// The underlying `JSObject` can be aliased, which can lead to
/// Undefined Behavior due to mutable aliasing.
pub unsafe fn as_mut_slice(&mut self) -> &mut [T::Element] {
let (pointer, length) = self.data();
slice::from_raw_parts_mut(pointer, length as usize)
}
}
impl<'a, T: TypedArrayElementCreator + TypedArrayElement> TypedArray<'a, T> {
/// Create a new JS typed array, optionally providing initial data that will
/// be copied into the newly-allocated buffer. Returns the new JS reflector.
pub unsafe fn create(cx: *mut JSContext,
with: CreateWith<T::Element>,
result: MutableHandleObject)
-> Result<(), ()> {
let length = match with {
CreateWith::Length(len) => len,
CreateWith::Slice(slice) => slice.len() as u32,
};
result.set(T::create_new(cx, length));
if result.get().is_null() {
return Err(());
}
if let CreateWith::Slice(data) = with {
TypedArray::<T>::update_raw(data, result.handle());
}
Ok(())
}
/// Update an existed JS typed array
pub unsafe fn update(&mut self, data: &[T::Element]) {
TypedArray::<T>::update_raw(data, self.object.handle());
}
unsafe fn update_raw(data: &[T::Element], result: HandleObject) {
let (buf, length) = T::length_and_data(result.get());
assert!(data.len() <= length as usize);
ptr::copy_nonoverlapping(data.as_ptr(), buf, data.len());
}
}
/// Internal trait used to associate an element type with an underlying representation
/// and various functions required to manipulate typed arrays of that element type.
pub trait TypedArrayElement {
/// Underlying primitive representation of this element type.
type Element;
/// Unwrap a typed array JS reflector for this element type.
unsafe fn unwrap_array(obj: *mut JSObject) -> *mut JSObject;
/// Retrieve the length and data of a typed array's buffer for this element type.
unsafe fn length_and_data(obj: *mut JSObject) -> (*mut Self::Element, u32);
}
/// Internal trait for creating new typed arrays.
pub trait TypedArrayElementCreator: TypedArrayElement {
/// Create a new typed array.
unsafe fn create_new(cx: *mut JSContext, length: u32) -> *mut JSObject;
/// Get the data.
unsafe fn get_data(obj: *mut JSObject) -> *mut Self::Element;
}
macro_rules! typed_array_element {
($t: ident,
$element: ty,
$unwrap: ident,
$length_and_data: ident) => (
/// A kind of typed array.
pub struct $t;
impl TypedArrayElement for $t {
type Element = $element;
unsafe fn unwrap_array(obj: *mut JSObject) -> *mut JSObject {
$unwrap(obj)
}
unsafe fn length_and_data(obj: *mut JSObject) -> (*mut Self::Element, u32) {
let mut len = 0;
let mut shared = false;
let mut data = ptr::null_mut();
$length_and_data(obj, &mut len, &mut shared, &mut data);
assert!(!shared);
(data, len)
}
}
);
($t: ident,
$element: ty,
$unwrap: ident,
$length_and_data: ident,
$create_new: ident,
$get_data: ident) => (
typed_array_element!($t, $element, $unwrap, $length_and_data);
impl TypedArrayElementCreator for $t {
unsafe fn create_new(cx: *mut JSContext, length: u32) -> *mut JSObject {
$create_new(cx, length)
}
unsafe fn get_data(obj: *mut JSObject) -> *mut Self::Element {
let mut shared = false;
let data = $get_data(obj, &mut shared, ptr::null_mut());
assert!(!shared);
data
}
}
);
}
typed_array_element!(Uint8,
u8,
UnwrapUint8Array,
GetUint8ArrayLengthAndData,
JS_NewUint8Array,
JS_GetUint8ArrayData);
typed_array_element!(Uint16,
u16,
UnwrapUint16Array,
GetUint16ArrayLengthAndData,
JS_NewUint16Array,
JS_GetUint16ArrayData);
typed_array_element!(Uint32,
u32,
UnwrapUint32Array,
GetUint32ArrayLengthAndData,
JS_NewUint32Array,
JS_GetUint32ArrayData);
typed_array_element!(Int8,
i8,
UnwrapInt8Array,
GetInt8ArrayLengthAndData,
JS_NewInt8Array,
JS_GetInt8ArrayData);
typed_array_element!(Int16,
i16,
UnwrapInt16Array,
GetInt16ArrayLengthAndData,
JS_NewInt16Array,
JS_GetInt16ArrayData);
typed_array_element!(Int32,
i32,
UnwrapInt32Array,
GetInt32ArrayLengthAndData,
JS_NewInt32Array,
JS_GetInt32ArrayData);
typed_array_element!(Float32,
f32,
UnwrapFloat32Array,
GetFloat32ArrayLengthAndData,
JS_NewFloat32Array,
JS_GetFloat32ArrayData);
typed_array_element!(Float64,
f64,
UnwrapFloat64Array,
GetFloat64ArrayLengthAndData,
JS_NewFloat64Array,
JS_GetFloat64ArrayData);
typed_array_element!(ClampedU8,
u8,
UnwrapUint8ClampedArray,
GetUint8ClampedArrayLengthAndData,
JS_NewUint8ClampedArray,
JS_GetUint8ClampedArrayData);
typed_array_element!(ArrayBufferU8,
u8,
UnwrapArrayBuffer,
GetArrayBufferLengthAndData,
JS_NewArrayBuffer,
JS_GetArrayBufferData);
typed_array_element!(ArrayBufferViewU8,
u8,
UnwrapArrayBufferView,
GetArrayBufferViewLengthAndData);
/// The Uint8ClampedArray type.
pub type Uint8ClampedArray<'a> = TypedArray<'a, ClampedU8>;
/// The Uint8Array type.
pub type Uint8Array<'a> = TypedArray<'a, Uint8>;
/// The Int8Array type.
pub type Int8Array<'a> = TypedArray<'a, Int8>;
/// The Uint16Array type.
pub type Uint16Array<'a> = TypedArray<'a, Uint16>;
/// The Int16Array type.
pub type Int16Array<'a> = TypedArray<'a, Int16>;
/// The Uint32Array type.
pub type Uint32Array<'a> = TypedArray<'a, Uint32>;
/// The Int32Array type.
pub type Int32Array<'a> = TypedArray<'a, Int32>;
/// The Float32Array type.
pub type Float32Array<'a> = TypedArray<'a, Float32>;
/// The Float64Array type.
pub type Float64Array<'a> = TypedArray<'a, Float64>;
/// The ArrayBuffer type.
pub type ArrayBuffer<'a> = TypedArray<'a, ArrayBufferU8>;
/// The ArrayBufferView type
pub type ArrayBufferView<'a> = TypedArray<'a, ArrayBufferViewU8>;
impl<'a> ArrayBufferView<'a> {
pub fn get_array_type(&self) -> Scalar::Type {
unsafe { JS_GetArrayBufferViewType(self.object.get()) }
}
}
#[macro_export]
macro_rules! typedarray {
(in($cx:expr) let $name:ident : $ty:ident = $init:expr) => {
let mut __root = $crate::jsapi::JS::Rooted::new_unrooted();
let $name = $crate::typedarray::$ty::from($cx, &mut __root, $init);
};
(in($cx:expr) let mut $name:ident : $ty:ident = $init:expr) => {
let mut __root = $crate::jsapi::JS::Rooted::new_unrooted();
let mut $name = $crate::typedarray::$ty::from($cx, &mut __root, $init);
}
}

64
js/rust/tests/callback.rs Normal file
View File

@ -0,0 +1,64 @@
/* 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/. */
#[macro_use]
extern crate js;
extern crate libc;
use js::ac::AutoCompartment;
use js::jsapi::root::JS::CallArgs;
use js::jsapi::root::JS::CompartmentOptions;
use js::jsapi::root::JSContext;
use js::jsapi::root::JS_DefineFunction;
use js::jsapi::root::JS_EncodeStringToUTF8;
use js::jsapi::root::JS_NewGlobalObject;
use js::jsapi::root::JS_ReportErrorASCII;
use js::jsapi::root::JS::OnNewGlobalHookOption;
use js::jsapi::root::JS::Value;
use js::jsval::UndefinedValue;
use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
use std::ffi::CStr;
use std::ptr;
use std::str;
#[test]
fn callback() {
let runtime = Runtime::new().unwrap();
let context = runtime.cx();
let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;
let c_option = CompartmentOptions::default();
unsafe {
let global = JS_NewGlobalObject(context, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(), h_option, &c_option);
rooted!(in(context) let global_root = global);
let global = global_root.handle();
let _ac = AutoCompartment::with_obj(context, global.get());
let function = JS_DefineFunction(context, global, b"puts\0".as_ptr() as *const libc::c_char,
Some(puts), 1, 0);
assert!(!function.is_null());
let javascript = "puts('Test Iñtërnâtiônàlizætiøn ┬─┬ノ( º _ ºノ) ');";
rooted!(in(context) let mut rval = UndefinedValue());
let _ = runtime.evaluate_script(global, javascript, "test.js", 0, rval.handle_mut());
}
}
unsafe extern "C" fn puts(context: *mut JSContext, argc: u32, vp: *mut Value) -> bool {
let args = CallArgs::from_vp(vp, argc);
if args._base.argc_ != 1 {
JS_ReportErrorASCII(context, b"puts() requires exactly 1 argument\0".as_ptr() as *const libc::c_char);
return false;
}
let arg = args.get(0);
let js = js::rust::ToString(context, arg);
rooted!(in(context) let message_root = js);
let message = JS_EncodeStringToUTF8(context, message_root.handle());
let message = CStr::from_ptr(message);
println!("{}", str::from_utf8(message.to_bytes()).unwrap());
args.rval().set(UndefinedValue());
return true;
}

View File

@ -0,0 +1,56 @@
/* 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/. */
#[macro_use]
extern crate js;
use js::glue::RUST_JSID_IS_STRING;
use js::glue::RUST_JSID_TO_STRING;
use js::jsapi::root::JS::CompartmentOptions;
use js::jsapi::root::js::GetPropertyKeys;
use js::jsapi::root::JSITER_OWNONLY;
use js::jsapi::root::JS_NewGlobalObject;
use js::jsapi::root::JS_StringEqualsAscii;
use js::jsapi::root::JS::OnNewGlobalHookOption;
use js::jsval::UndefinedValue;
use js::rust::IdVector;
use js::rust::Runtime;
use js::rust::SIMPLE_GLOBAL_CLASS;
use std::ptr;
#[test]
fn enumerate() {
let rt = Runtime::new().unwrap();
let cx = rt.cx();
unsafe {
rooted!(in(cx) let global =
JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(),
OnNewGlobalHookOption::FireOnNewGlobalHook,
&CompartmentOptions::default())
);
rooted!(in(cx) let mut rval = UndefinedValue());
assert!(rt.evaluate_script(global.handle(), "({ 'a': 7 })",
"test", 1, rval.handle_mut()).is_ok());
assert!(rval.is_object());
rooted!(in(cx) let object = rval.to_object());
let ids = IdVector::new(cx);
assert!(GetPropertyKeys(cx, object.handle(), JSITER_OWNONLY, ids.get()));
assert_eq!(ids.len(), 1);
rooted!(in(cx) let id = ids[0]);
assert!(RUST_JSID_IS_STRING(id.handle()));
rooted!(in(cx) let id = RUST_JSID_TO_STRING(id.handle()));
let mut matches = false;
assert!(JS_StringEqualsAscii(cx,
id.get(),
b"a\0" as *const _ as *const _,
&mut matches));
assert!(matches);
}
}

32
js/rust/tests/evaluate.rs Normal file
View File

@ -0,0 +1,32 @@
/* 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/. */
#[macro_use]
extern crate js;
use js::jsapi::root::JS::CompartmentOptions;
use js::jsapi::root::JS_NewGlobalObject;
use js::jsapi::root::JS::OnNewGlobalHookOption;
use js::jsval::UndefinedValue;
use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
use std::ptr;
#[test]
fn evaluate() {
let rt = Runtime::new().unwrap();
let cx = rt.cx();
unsafe {
rooted!(in(cx) let global =
JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(),
OnNewGlobalHookOption::FireOnNewGlobalHook,
&CompartmentOptions::default())
);
rooted!(in(cx) let mut rval = UndefinedValue());
assert!(rt.evaluate_script(global.handle(), "1 + 1",
"test", 1, rval.handle_mut()).is_ok());
}
}

43
js/rust/tests/panic.rs Normal file
View File

@ -0,0 +1,43 @@
/* 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/. */
#[macro_use]
extern crate js;
use js::jsapi::*;
use js::jsval::UndefinedValue;
use js::panic::wrap_panic;
use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
use std::ptr;
use std::str;
#[test]
#[should_panic]
fn panic() {
let runtime = Runtime::new().unwrap();
let context = runtime.cx();
let h_option = JS::OnNewGlobalHookOption::FireOnNewGlobalHook;
let c_option = JS::CompartmentOptions::default();
unsafe {
let global = JS_NewGlobalObject(context, &SIMPLE_GLOBAL_CLASS,
ptr::null_mut(), h_option, &c_option);
rooted!(in(context) let global_root = global);
let global = global_root.handle();
let _ac = js::ac::AutoCompartment::with_obj(context, global.get());
let function = JS_DefineFunction(context, global,
b"test\0".as_ptr() as *const _,
Some(test), 0, 0);
assert!(!function.is_null());
rooted!(in(context) let mut rval = UndefinedValue());
let _ = runtime.evaluate_script(global, "test();", "test.js", 0,
rval.handle_mut());
}
}
unsafe extern "C" fn test(_cx: *mut JSContext, _argc: u32, _vp: *mut JS::Value) -> bool {
wrap_panic(|| {
panic!()
}, false)
}

83
js/rust/tests/rooting.rs Normal file
View File

@ -0,0 +1,83 @@
/* 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/. */
#![cfg(feature = "debugmozjs")]
#[macro_use]
extern crate js;
#[macro_use]
extern crate lazy_static;
extern crate libc;
use js::jsapi::*;
use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS, define_methods};
use std::ptr;
#[test]
fn rooting() {
unsafe {
let runtime = Runtime::new().unwrap();
JS_SetGCZeal(runtime.cx(), 2, 1);
let cx = runtime.cx();
let h_option = JS::OnNewGlobalHookOption::FireOnNewGlobalHook;
let c_option = JS::CompartmentOptions::default();
rooted!(in(cx) let global = JS_NewGlobalObject(cx,
&SIMPLE_GLOBAL_CLASS,
ptr::null_mut(),
h_option,
&c_option));
let _ac = js::ac::AutoCompartment::with_obj(cx, global.get());
rooted!(in(cx) let prototype_proto = JS_GetObjectPrototype(cx, global.handle()));
rooted!(in(cx) let proto = JS_NewObjectWithUniqueType(cx,
&CLASS as *const _,
prototype_proto.handle()));
define_methods(cx, proto.handle(), &METHODS[..]).unwrap();
}
}
unsafe extern "C" fn generic_method(_: *mut JSContext, _: u32, _: *mut JS::Value) -> bool {
true
}
lazy_static! {
static ref METHODS: [JSFunctionSpec; 4] = [
JSFunctionSpec {
name: b"addEventListener\0" as *const u8 as *const libc::c_char,
call: JSNativeWrapper { op: Some(generic_method), info: ptr::null() },
nargs: 2,
flags: JSPROP_ENUMERATE as u16,
selfHostedName: 0 as *const libc::c_char
},
JSFunctionSpec {
name: b"removeEventListener\0" as *const u8 as *const libc::c_char,
call: JSNativeWrapper { op: Some(generic_method), info: ptr::null() },
nargs: 2,
flags: JSPROP_ENUMERATE as u16,
selfHostedName: 0 as *const libc::c_char
},
JSFunctionSpec {
name: b"dispatchEvent\0" as *const u8 as *const libc::c_char,
call: JSNativeWrapper { op: Some(generic_method), info: ptr::null() },
nargs: 1,
flags: JSPROP_ENUMERATE as u16,
selfHostedName: 0 as *const libc::c_char
},
JSFunctionSpec {
name: ptr::null(),
call: JSNativeWrapper { op: None, info: ptr::null() },
nargs: 0,
flags: 0,
selfHostedName: ptr::null()
}
];
}
static CLASS: JSClass = JSClass {
name: b"EventTargetPrototype\0" as *const u8 as *const libc::c_char,
flags: 0,
cOps: 0 as *const _,
reserved: [0 as *mut _; 3]
};

57
js/rust/tests/runtime.rs Normal file
View File

@ -0,0 +1,57 @@
/* 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/. */
#[macro_use]
extern crate js;
extern crate libc;
use js::jsapi::*;
use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
use std::ptr;
#[test]
fn runtime() {
unsafe {
let runtime = Runtime::new().unwrap();
let cx = runtime.cx();
let h_option = JS::OnNewGlobalHookOption::FireOnNewGlobalHook;
let c_option = JS::CompartmentOptions::default();
rooted!(in(cx) let global = JS_NewGlobalObject(cx,
&SIMPLE_GLOBAL_CLASS,
ptr::null_mut(),
h_option,
&c_option));
let _ac = js::ac::AutoCompartment::with_obj(cx, global.get());
rooted!(in(cx) let _object = JS_NewObject(cx, &CLASS as *const _));
}
assert!(Runtime::new().is_err());
}
unsafe extern fn finalize(_fop: *mut JSFreeOp, _object: *mut JSObject) {
assert!(!Runtime::get().is_null());
}
static CLASS_OPS: JSClassOps = JSClassOps {
addProperty: None,
delProperty: None,
enumerate: None,
newEnumerate: None,
resolve: None,
mayResolve: None,
finalize: Some(finalize),
call: None,
hasInstance: None,
construct: None,
trace: None,
};
static CLASS: JSClass = JSClass {
name: b"EventTargetPrototype\0" as *const u8 as *const libc::c_char,
flags: 0 | JSCLASS_FOREGROUND_FINALIZE,
cOps: &CLASS_OPS as *const JSClassOps,
reserved: [0 as *mut _; 3]
};

View File

@ -0,0 +1,32 @@
/* 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/. */
#[macro_use]
extern crate js;
use js::jsapi::root::JS::CompartmentOptions;
use js::jsapi::root::JS_NewGlobalObject;
use js::jsapi::root::JS::OnNewGlobalHookOption;
use js::jsval::UndefinedValue;
use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
use std::ptr;
#[test]
fn stack_limit() {
let rt = Runtime::new().unwrap();
let cx = rt.cx();
unsafe {
let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;
let c_option = CompartmentOptions::default();
let global = JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS,
ptr::null_mut(), h_option, &c_option);
rooted!(in(cx) let global_root = global);
let global = global_root.handle();
rooted!(in(cx) let mut rval = UndefinedValue());
assert!(rt.evaluate_script(global, "function f() { f.apply() } f()",
"test", 1, rval.handle_mut()).is_err());
}
}

View File

@ -0,0 +1,91 @@
/* 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/. */
#[macro_use]
extern crate js;
use js::jsapi::*;
use js::jsval::UndefinedValue;
use js::rust::Runtime as Runtime_;
use js::rust::SIMPLE_GLOBAL_CLASS;
use js::typedarray::{CreateWith, Uint32Array};
use std::ptr;
#[test]
fn typedarray() {
let rt = Runtime_::new().unwrap();
let cx = rt.cx();
unsafe {
rooted!(in(cx) let global =
JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(),
JS::OnNewGlobalHookOption::FireOnNewGlobalHook,
&JS::CompartmentOptions::default())
);
let _ac = js::ac::AutoCompartment::with_obj(cx, global.get());
rooted!(in(cx) let mut rval = UndefinedValue());
assert!(rt.evaluate_script(global.handle(), "new Uint8Array([0, 2, 4])",
"test", 1, rval.handle_mut()).is_ok());
assert!(rval.is_object());
typedarray!(in(cx) let array: Uint8Array = rval.to_object());
assert_eq!(array.unwrap().as_slice(), &[0, 2, 4][..]);
typedarray!(in(cx) let array: Uint16Array = rval.to_object());
assert!(array.is_err());
typedarray!(in(cx) let view: ArrayBufferView = rval.to_object());
assert_eq!(view.unwrap().get_array_type(), js::jsapi::js::Scalar::Type::Uint8);
rooted!(in(cx) let mut rval = ptr::null_mut());
assert!(Uint32Array::create(cx, CreateWith::Slice(&[1, 3, 5]), rval.handle_mut()).is_ok());
typedarray!(in(cx) let array: Uint32Array = rval.get());
assert_eq!(array.unwrap().as_slice(), &[1, 3, 5][..]);
typedarray!(in(cx) let mut array: Uint32Array = rval.get());
array.as_mut().unwrap().update(&[2, 4, 6]);
assert_eq!(array.unwrap().as_slice(), &[2, 4, 6][..]);
rooted!(in(cx) let rval = ptr::null_mut());
typedarray!(in(cx) let array: Uint8Array = rval.get());
assert!(array.is_err());
rooted!(in(cx) let mut rval = ptr::null_mut());
assert!(Uint32Array::create(cx, CreateWith::Length(5), rval.handle_mut()).is_ok());
typedarray!(in(cx) let array: Uint32Array = rval.get());
assert_eq!(array.unwrap().as_slice(), &[0, 0, 0, 0, 0]);
typedarray!(in(cx) let mut array: Uint32Array = rval.get());
array.as_mut().unwrap().update(&[0, 1, 2, 3]);
assert_eq!(array.unwrap().as_slice(), &[0, 1, 2, 3, 0]);
typedarray!(in(cx) let view: ArrayBufferView = rval.get());
assert_eq!(view.unwrap().get_array_type(), js::jsapi::js::Scalar::Type::Uint32);
}
}
#[test]
#[should_panic]
fn typedarray_update_panic() {
let rt = Runtime_::new().unwrap();
let cx = rt.cx();
unsafe {
rooted!(in(cx) let global =
JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(),
JS::OnNewGlobalHookOption::FireOnNewGlobalHook,
&JS::CompartmentOptions::default())
);
let _ac = js::ac::AutoCompartment::with_obj(cx, global.get());
rooted!(in(cx) let mut rval = ptr::null_mut());
let _ = Uint32Array::create(cx, CreateWith::Slice(&[1, 2, 3, 4, 5]), rval.handle_mut());
typedarray!(in(cx) let mut array: Uint32Array = rval.get());
array.as_mut().unwrap().update(&[0, 2, 4, 6, 8, 10]);
}
}

View File

@ -0,0 +1,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/. */
#[macro_use]
extern crate js;
use js::ac::AutoCompartment;
use js::conversions::ConversionBehavior;
use js::conversions::ConversionResult;
use js::conversions::FromJSValConvertible;
use js::conversions::ToJSValConvertible;
use js::jsapi::root::JS::CompartmentOptions;
use js::jsapi::root::JS_InitStandardClasses;
use js::jsapi::root::JS_NewGlobalObject;
use js::jsapi::root::JS::OnNewGlobalHookOption;
use js::jsval::UndefinedValue;
use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
use std::ptr;
fn assert_is_array(cx: *mut js::jsapi::root::JSContext,
val: js::jsapi::root::JS::HandleValue) {
let mut is_array = false;
assert!(unsafe {
js::jsapi::root::JS_IsArrayObject(cx, val, &mut is_array as *mut _)
});
assert!(is_array);
}
#[test]
fn vec_conversion() {
let rt = Runtime::new().unwrap();
let cx = rt.cx();
let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;
let c_option = CompartmentOptions::default();
unsafe {
let global = JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS,
ptr::null_mut(), h_option, &c_option);
rooted!(in(cx) let global_root = global);
let global = global_root.handle();
let _ac = AutoCompartment::with_obj(cx, global.get());
assert!(JS_InitStandardClasses(cx, global));
rooted!(in(cx) let mut rval = UndefinedValue());
let orig_vec: Vec<f32> = vec![1.0, 2.9, 3.0];
orig_vec.to_jsval(cx, rval.handle_mut());
assert_is_array(cx, rval.handle());
let converted = Vec::<f32>::from_jsval(cx, rval.handle(), ()).unwrap();
assert_eq!(&orig_vec, converted.get_success_value().unwrap());
let orig_vec: Vec<i32> = vec![1, 2, 3];
orig_vec.to_jsval(cx, rval.handle_mut());
assert_is_array(cx, rval.handle());
let converted = Vec::<i32>::from_jsval(cx, rval.handle(),
ConversionBehavior::Default).unwrap();
assert_eq!(&orig_vec, converted.get_success_value().unwrap());
rt.evaluate_script(global, "new Set([1, 2, 3])",
"test", 1, rval.handle_mut()).unwrap();
let converted =
Vec::<i32>::from_jsval(cx, rval.handle(),
ConversionBehavior::Default).unwrap();
assert_eq!(&orig_vec, converted.get_success_value().unwrap());
rt.evaluate_script(global, "({})", "test", 1, rval.handle_mut()).unwrap();
let converted = Vec::<i32>::from_jsval(cx, rval.handle(),
ConversionBehavior::Default);
assert!(match converted {
Ok(ConversionResult::Failure(_)) => true,
_ => false,
});
}
}

13
js/src/Cargo.lock generated
View File

@ -3,7 +3,7 @@ name = "mozjs_sys"
version = "0.0.0"
dependencies = [
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
"libz-sys 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
"libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -19,12 +19,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libz-sys"
version = "1.0.12"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gcc 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -40,9 +41,15 @@ name = "pkg-config"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vcpkg"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum gcc 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)" = "291055c78f59ca3d84c99026c9501c469413d386bb46be1e1cf1d285cd1db3b0"
"checksum libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)" = "38f5c2b18a287cf78b4097db62e20f43cace381dc76ae5c0a3073067f78b7ddc"
"checksum libz-sys 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "7616099a575493da60cddc1174b686fcfb00ece89dc6f61f31ff47c35f07bbe8"
"checksum libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd64ef8ee652185674455c1d450b83cbc8ad895625d543b5324d923f82e4d8"
"checksum num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a225d1e2717567599c24f88e49f00856c6e825a12125181ee42c4257e3688d39"
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
"checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b"

View File

@ -18,4 +18,4 @@ num_cpus = "1.1.0"
[dependencies]
libc = "0.2"
libz-sys = "1.0"
libz-sys = "1.0.16"

View File

@ -113,10 +113,10 @@ class InlineTable
friend class InlineTable;
protected:
Entry entry_;
TablePtr tablePtr_;
InlineEntry* inlPtr_;
bool isInlinePtr_;
MOZ_INIT_OUTSIDE_CTOR Entry entry_;
MOZ_INIT_OUTSIDE_CTOR TablePtr tablePtr_;
MOZ_INIT_OUTSIDE_CTOR InlineEntry* inlPtr_;
MOZ_INIT_OUTSIDE_CTOR bool isInlinePtr_;
explicit Ptr(TablePtr p)
: entry_(p.found() ? &*p : nullptr),
@ -180,12 +180,12 @@ class InlineTable
friend class InlineTable;
protected:
Entry entry_;
TableAddPtr tableAddPtr_;
InlineEntry* inlAddPtr_;
bool isInlinePtr_;
MOZ_INIT_OUTSIDE_CTOR Entry entry_;
MOZ_INIT_OUTSIDE_CTOR TableAddPtr tableAddPtr_;
MOZ_INIT_OUTSIDE_CTOR InlineEntry* inlAddPtr_;
MOZ_INIT_OUTSIDE_CTOR bool isInlinePtr_;
// Indicates whether inlAddPtr is a found result or an add pointer.
bool inlPtrFound_;
MOZ_INIT_OUTSIDE_CTOR bool inlPtrFound_;
AddPtr(InlineEntry* ptr, bool found)
: entry_(ptr),

View File

@ -27,6 +27,7 @@ class NameResolver
JSContext* cx;
size_t nparents; /* number of parents in the parents array */
MOZ_INIT_OUTSIDE_CTOR
ParseNode* parents[MaxParents]; /* history of ParseNodes we've been looking at */
StringBuffer* buf; /* when resolving, buffer to append to */

View File

@ -2818,6 +2818,9 @@ Simulator::decodeTypeRegister(SimInstruction* instr)
MOZ_CRASH();
break;
case ff_movf_fmt:
// location of cc field in MOVF is equal to float branch instructions
cc = instr->fbccValue();
fcsr_cc = GetFCSRConditionBit(cc);
if (testFCSRBit(fcsr_cc)) {
setFpuRegisterFloat(fd_reg, getFpuRegisterFloat(fs_reg));
}
@ -2959,6 +2962,9 @@ Simulator::decodeTypeRegister(SimInstruction* instr)
MOZ_CRASH();
break;
case ff_movf_fmt:
// location of cc field in MOVF is equal to float branch instructions
cc = instr->fbccValue();
fcsr_cc = GetFCSRConditionBit(cc);
if (testFCSRBit(fcsr_cc)) {
setFpuRegisterDouble(fd_reg, getFpuRegisterDouble(fs_reg));
}

View File

@ -3041,6 +3041,9 @@ Simulator::decodeTypeRegister(SimInstruction* instr)
}
break;
case ff_movf_fmt:
// location of cc field in MOVF is equal to float branch instructions
cc = instr->fbccValue();
fcsr_cc = GetFCSRConditionBit(cc);
if (testFCSRBit(fcsr_cc)) {
setFpuRegisterDouble(fd_reg, getFpuRegisterDouble(fs_reg));
}

View File

@ -110,6 +110,7 @@ case $cmd in
cp -pPR ${TOPSRCDIR}/mfbt ${tgtpath}
cp -p ${SRCDIR}/../moz.configure ${tgtpath}/js
cp -pPR ${SRCDIR}/../public ${tgtpath}/js
cp -pPR ${SRCDIR}/../rust ${tgtpath}/js
cp -pPR ${SRCDIR}/../examples ${tgtpath}/js
find ${SRCDIR} -mindepth 1 -maxdepth 1 -not -path ${STAGING} -a -not -name ${pkg} \
-exec cp -pPR {} ${tgtpath}/js/src \;

View File

@ -1459,7 +1459,8 @@ class CalleeDesc
};
private:
Which which_;
// which_ shall be initialized in the static constructors
MOZ_INIT_OUTSIDE_CTOR Which which_;
union U {
U() {}
uint32_t funcIndex_;

View File

@ -92,4 +92,4 @@ fuzzy-if(gtkWidget,1,9) fuzzy-if(winWidget&&!d2d,1,9) fuzzy-if(d2d,5,40) fuzzy-i
== color-layer-1a.html color-layer-1-ref.html
fuzzy-if(webrender,1-1,97-97) == corner-split.html corner-split-ref.svg # bug 1185636
fuzzy-if(webrender,1-1,97-105) == corner-split.html corner-split-ref.svg # bug 1185636

View File

@ -51,5 +51,5 @@
fails-if(styloVsGecko||stylo) == xbl-children-1.xhtml xbl-children-1-ref.xhtml # Bug 1374247
== xbl-children-2.xhtml about:blank
fuzzy-if(webrender,8-8,1036-1036) == xbl-children-3.xhtml xbl-children-3-ref.html
fuzzy-if(webrender,3-8,1036-1036) == xbl-children-3.xhtml xbl-children-3-ref.html
== xbl-children-4.xhtml about:blank

View File

@ -9,6 +9,6 @@ default-preferences pref(dom.forms.color,true)
fails-if(Android) == margin-padding-1.html margin-padding-1-ref.html
== block-invalidate-1.html block-invalidate-1-ref.html
== block-invalidate-2.html block-invalidate-2-ref.html
fuzzy-if(gtkWidget,8,33) fuzzy-if(skiaContent,8,78) fails-if(Android) == transformations-1.html transformations-1-ref.html
fuzzy-if(gtkWidget,8,33) fuzzy-if(skiaContent,8,80) fails-if(Android) == transformations-1.html transformations-1-ref.html
fails-if(Android) == custom-style-1.html custom-style-1-ref.html
fails-if(Android) == custom-style-2.html custom-style-2-ref.html

View File

@ -148,7 +148,7 @@ load 705996-2.html
load 707622-1.html
load 710098-1.html
load 711864-1.html
asserts-if(gtkWidget&&browserIsRemote,5) load 759249-1.html # Bug 1195474
load 759249-1.html
load 759249-2.html
load 814713.html
load 980223.html

View File

@ -63,7 +63,7 @@ load 404192.xhtml
load 407152.xul
load 408904-1.xul
load 412479-1.xhtml
asserts(4) asserts-if(gtkWidget&&browserIsRemote,6) asserts-if(stylo,4-6) load 415394-1.xhtml # Bug 163838, bug 1195474 # bug 1324651
load 415394-1.xhtml
load 417509.xul
load 420424-1.xul
load 430356-1.xhtml

View File

@ -286,6 +286,9 @@ public class GeckoViewActivity extends Activity {
final TargetWindow where) {
Log.d(LOGTAG, "onLoadUri=" + uri +
" where=" + where);
if (where != TargetWindow.NEW) {
return false;
}
view.loadUri(uri);
return true;
}

View File

@ -24,16 +24,16 @@ function debug(aMsg) {
}
// Handles navigation requests between Gecko and a GeckoView.
// Implements GeckoView.loadUri via openURI.
// Handles GeckoView:GoBack and :GoForward requests dispatched by
// GeckoView.goBack and .goForward.
// Dispatches GeckoView:LocationChange to the GeckoView on location change when
// active.
// Can be activated and deactivated by GeckoViewNavigation:Active and :Inactive
// events.
// Implements nsIBrowserDOMWindow.
// Implements nsILoadURIDelegate.
class GeckoViewNavigation extends GeckoViewModule {
init() {
this.window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = this;
this.browser.docShell.loadURIDelegate = this;
this.eventDispatcher.registerListener(this, [
"GeckoView:GoBack",
@ -72,14 +72,13 @@ class GeckoViewNavigation extends GeckoViewModule {
debug("receiveMessage " + aMsg.name);
}
// nsIBrowserDOMWindow::createContentWindow implementation.
createContentWindow(aUri, aOpener, aWhere, aFlags, aTriggeringPrincipal) {
debug("createContentWindow: aUri=" + (aUri && aUri.spec) +
handleLoadUri(aUri, aOpener, aWhere, aFlags, aTriggeringPrincipal) {
debug("handleOpenURI: aUri=" + (aUri && aUri.spec) +
" aWhere=" + aWhere +
" aFlags=" + aFlags);
if (!aUri) {
throw Cr.NS_ERROR_ABORT;
return false;
}
let message = {
@ -97,6 +96,29 @@ class GeckoViewNavigation extends GeckoViewModule {
});
Services.tm.spinEventLoopUntil(() => handled !== undefined);
return handled;
}
// nsILoadURIDelegate.
loadURI(aUri, aWhere, aFlags, aTriggeringPrincipal) {
debug("loadURI " + aUri + " " + aWhere + " " + aFlags + " " +
aTriggeringPrincipal);
let handled = this.handleLoadUri(aUri, null, aWhere, aFlags,
aTriggeringPrincipal);
if (!handled) {
throw Cr.NS_ERROR_ABORT;
}
}
// nsIBrowserDOMWindow.
createContentWindow(aUri, aOpener, aWhere, aFlags, aTriggeringPrincipal) {
debug("createContentWindow: aUri=" + (aUri && aUri.spec) +
" aWhere=" + aWhere +
" aFlags=" + aFlags);
let handled = this.handleLoadUri(aUri, aOpener, aWhere, aFlags,
aTriggeringPrincipal);
if (!handled &&
(aWhere === Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW ||
aWhere === Ci.nsIBrowserDOMWindow.OPEN_CURRENTWINDOW)) {

Some files were not shown because too many files have changed in this diff Show More