mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-01 17:23:59 +00:00
merge mozilla-central to autoland. r=merge a=merge
This commit is contained in:
commit
4d2231acf0
@ -437,9 +437,9 @@
|
||||
position="bottomcenter topright"
|
||||
tabspecific="true"
|
||||
noautofocus="true"
|
||||
copyURL-title="©URLCmd.label;"
|
||||
copyURL-title="©LinkCmd.label;"
|
||||
emailLink-title="&emailPageCmd.label;"
|
||||
sendToDevice-title="&sendToDevice.label3;"
|
||||
sendToDevice-title="&sendTabToDevice.label;"
|
||||
sendToDevice-notReadyTitle="&sendToDevice.syncNotReady.label;">
|
||||
<photonpanelmultiview id="pageActionPanelMultiView"
|
||||
mainViewId="pageActionPanelMainView"
|
||||
|
@ -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"/>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
window.onbeforeunload = function() {
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
TEST PAGE
|
||||
</body>
|
||||
</html>
|
@ -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);
|
||||
});
|
||||
|
@ -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">
|
||||
|
@ -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]
|
||||
|
@ -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.");
|
||||
});
|
@ -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>
|
@ -308,7 +308,6 @@ CanvasFrameAnonymousContentHelper.prototype = {
|
||||
*/
|
||||
_onWindowReady(e, {isTopLevel}) {
|
||||
if (isTopLevel) {
|
||||
this._remove();
|
||||
this._removeAllListeners();
|
||||
this.elements.clear();
|
||||
this._insert();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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?");
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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. */
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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.)
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
%}
|
||||
};
|
||||
|
@ -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
1
js/rust/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
target/
|
35
js/rust/CMakeLists.txt
Normal file
35
js/rust/CMakeLists.txt
Normal 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
494
js/rust/Cargo.lock
generated
Normal 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
50
js/rust/Cargo.toml
Normal 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
46
js/rust/README.md
Normal 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
468
js/rust/build.rs
Normal 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
23
js/rust/etc/wrapper.hpp
Normal 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
56
js/rust/src/ac.rs
Normal 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
679
js/rust/src/conversions.rs
Normal 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
73
js/rust/src/error.rs
Normal 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
348
js/rust/src/glue.rs
Normal 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
172
js/rust/src/heap.rs
Normal 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
5
js/rust/src/jsapi.rs
Normal 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
940
js/rust/src/jsglue.cpp
Normal 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
549
js/rust/src/jsval.rs
Normal 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
54
js/rust/src/lib.rs
Normal 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
33
js/rust/src/panic.rs
Normal 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
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
102
js/rust/src/sc.rs
Normal 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
301
js/rust/src/typedarray.rs
Normal 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
64
js/rust/tests/callback.rs
Normal 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;
|
||||
}
|
56
js/rust/tests/enumerate.rs
Normal file
56
js/rust/tests/enumerate.rs
Normal 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
32
js/rust/tests/evaluate.rs
Normal 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
43
js/rust/tests/panic.rs
Normal 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
83
js/rust/tests/rooting.rs
Normal 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
57
js/rust/tests/runtime.rs
Normal 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]
|
||||
};
|
32
js/rust/tests/stack_limit.rs
Normal file
32
js/rust/tests/stack_limit.rs
Normal 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());
|
||||
}
|
||||
}
|
91
js/rust/tests/typedarray.rs
Normal file
91
js/rust/tests/typedarray.rs
Normal 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]);
|
||||
}
|
||||
}
|
80
js/rust/tests/vec_conversion.rs
Normal file
80
js/rust/tests/vec_conversion.rs
Normal 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
13
js/src/Cargo.lock
generated
@ -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"
|
||||
|
@ -18,4 +18,4 @@ num_cpus = "1.1.0"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
libz-sys = "1.0"
|
||||
libz-sys = "1.0.16"
|
||||
|
@ -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),
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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 \;
|
||||
|
@ -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_;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user