Bug 1892481 - More consistently enforce noscript and builtinclass in xpidl, r=xpcom-reviewers,necko-reviewers,valentin,media-playback-reviewers,mccr8,padenot

These properties were previously being checked much later, during xptcodegen,
and causing methods to be treated as noscript implicitly.

This change enforces the noscript requirements earlier when possible in
xpidl.py, to produce better errors, and adds addiitonal checks to ensure that
types which need to be builtinclass are marked as such.

This required some changes to interfaces in order to satisfy the new checks.

Differential Revision: https://phabricator.services.mozilla.com/D207804
This commit is contained in:
Nika Layzell 2024-04-22 18:19:58 +00:00
parent 505ed486af
commit b3f3e9a04a
29 changed files with 135 additions and 73 deletions

View File

@ -498,7 +498,7 @@ interface nsIPrincipal : nsISupports
*
* May be called from any thread.
*/
nsIReferrerInfo createReferrerInfo(in ReferrerPolicy aReferrerPolicy);
[noscript] nsIReferrerInfo createReferrerInfo(in ReferrerPolicy aReferrerPolicy);
/**
* The base part of |origin| without the concatenation with |originSuffix|.

View File

@ -211,8 +211,9 @@ interface nsIDocumentViewer : nsISupports
/**
* Sets the print settings for print / print-previewing a subdocument.
*/
[can_run_script] void setPrintSettingsForSubdocument(in nsIPrintSettings aPrintSettings,
in RemotePrintJobChildPtr aRemotePrintJob);
[can_run_script, noscript]
void setPrintSettingsForSubdocument(in nsIPrintSettings aPrintSettings,
in RemotePrintJobChildPtr aRemotePrintJob);
/**
* Get the history entry that this viewer will save itself into when

View File

@ -57,7 +57,7 @@ native GetGMPVideoEncoderCallback(mozilla::UniquePtr<GetGMPVideoEncoderCallback>
native GetNodeIdCallback(mozilla::UniquePtr<GetNodeIdCallback>&&);
native GMPCrashHelperPtr(mozilla::GMPCrashHelper*);
[scriptable, uuid(44d362ae-937a-4803-bee6-f2512a0149d1)]
[scriptable, builtinclass, uuid(44d362ae-937a-4803-bee6-f2512a0149d1)]
interface mozIGeckoMediaPluginService : nsISupports
{

View File

@ -11,12 +11,6 @@
#include "domstubs.idl"
%{C++
#include "nsTArrayForwardDeclare.h"
%}
[ref] native nsUint8TArrayRef(nsTArray<uint8_t>);
[ptr] native JSContextPtr(JSContext);
/*
* This interface is implemented in TCPSocket.cpp as an internal interface
@ -36,7 +30,7 @@ interface nsITCPSocketCallback : nsISupports {
void fireDataStringEvent(in AString type, in ACString data);
// Dispatch a "data" event at this object with an Array
void fireDataArrayEvent(in AString type, [const] in nsUint8TArrayRef data);
void fireDataArrayEvent(in AString type, in Array<uint8_t> data);
// Dispatch an event of the given type at this object.
void fireEvent(in AString type);

View File

@ -37,8 +37,8 @@ interface nsIXPCTestTypeScript : nsISupports {
void exposedMethod(in long arg);
// Members referencing TSNoncompat typedefs are not exposed.
attribute Noncompat noncompatProp;
void noncompatMethod(in Noncompat arg);
[noscript] attribute Noncompat noncompatProp;
[noscript] void noncompatMethod(in Noncompat arg);
// [noscript] attributes and methods are not exposed.
[noscript] attribute long noscriptProp;

View File

@ -85,7 +85,7 @@ interface nsIZipReader : nsISupports
* ZipReader is closed or destroyed, and must free the memory as
* appropriate afterwards.
*/
void openMemory(in voidPtr aData, in unsigned long aLength);
[noscript] void openMemory(in voidPtr aData, in unsigned long aLength);
/**
* The file that represents the zip with which this zip reader was
@ -238,7 +238,7 @@ interface nsIZipReaderCache : nsISupports
* Returns the cached NSPR file descriptor of the file.
* Note: currently not supported on Windows platform.
*/
PRFileDescStar getFd(in nsIFile zipFile);
[noscript] PRFileDescStar getFd(in nsIFile zipFile);
};
////////////////////////////////////////////////////////////////////////////////

View File

@ -22,7 +22,7 @@ class ClassOfService;
%}
native ClassOfService(mozilla::net::ClassOfService);
[scriptable, uuid(1ccb58ec-5e07-4cf9-a30d-ac5490d23b41)]
[scriptable, builtinclass, uuid(1ccb58ec-5e07-4cf9-a30d-ac5490d23b41)]
interface nsIClassOfService : nsISupports
{
attribute unsigned long classFlags;
@ -30,7 +30,7 @@ interface nsIClassOfService : nsISupports
void clearClassFlags(in unsigned long flags);
void addClassFlags(in unsigned long flags);
void setClassOfService(in ClassOfService s);
[noscript] void setClassOfService(in ClassOfService s);
// All these flags have a (de)prioritization effect.

View File

@ -21,7 +21,7 @@ native NetAddr(mozilla::net::NetAddr);
* This interface represents a native NetAddr struct in a readonly
* interface.
*/
[scriptable, uuid(652B9EC5-D159-45D7-9127-50BB559486CD)]
[scriptable, builtinclass, uuid(652B9EC5-D159-45D7-9127-50BB559486CD)]
interface nsINetAddr : nsISupports
{
/**

View File

@ -48,7 +48,7 @@ interface nsIInterceptedBodyCallback : nsISupports
* which do not implement nsIChannel.
*/
[scriptable, uuid(f4b82975-6a86-4cc4-87fe-9a1fd430c86d)]
[scriptable, builtinclass, uuid(f4b82975-6a86-4cc4-87fe-9a1fd430c86d)]
interface nsIInterceptedChannel : nsISupports
{
/**

View File

@ -19,7 +19,7 @@ typedef unsigned long nsServerSocketFlag;
*
* An interface to a server socket that can accept incoming connections.
*/
[scriptable, uuid(7a9c39cb-a13f-4eef-9bdf-a74301628742)]
[scriptable, builtinclass, uuid(7a9c39cb-a13f-4eef-9bdf-a74301628742)]
interface nsIServerSocket : nsISupports
{
/**

View File

@ -8,7 +8,7 @@ interface nsIX509Cert;
interface nsITLSServerSecurityObserver;
interface nsISocketTransport;
[scriptable, uuid(cc2c30f9-cfaa-4b8a-bd44-c24881981b74)]
[scriptable, builtinclass, uuid(cc2c30f9-cfaa-4b8a-bd44-c24881981b74)]
interface nsITLSServerSocket : nsIServerSocket
{
/**

View File

@ -25,7 +25,7 @@ interface nsIServerTiming : nsISupports {
[ref] native nsServerTimingArrayRef(nsTArray<nsCOMPtr<nsIServerTiming>>);
// All properties return zero if the value is not available
[scriptable, uuid(ca63784d-959c-4c3a-9a59-234a2a520de0)]
[scriptable, builtinclass, uuid(ca63784d-959c-4c3a-9a59-234a2a520de0)]
interface nsITimedChannel : nsISupports {
// Set this attribute to true to enable collection of timing data.
// channelCreationTime will be available even with this attribute set to
@ -124,5 +124,5 @@ interface nsITimedChannel : nsISupports {
[noscript] attribute boolean reportResourceTiming;
readonly attribute nsIArray serverTiming;
nsServerTimingArrayRef getNativeServerTiming();
[noscript] nsServerTimingArrayRef getNativeServerTiming();
};

View File

@ -31,7 +31,7 @@ native NetAddr(mozilla::net::NetAddr);
*
* An interface to a UDP socket that can accept incoming connections.
*/
[scriptable, uuid(d423bf4e-4499-40cf-bc03-153e2bf206d1)]
[scriptable, builtinclass, uuid(d423bf4e-4499-40cf-bc03-153e2bf206d1)]
interface nsIUDPSocket : nsISupports
{
/**
@ -126,7 +126,7 @@ interface nsIUDPSocket : nsISupports
* @param aRemoteAddr
* The remote address to connect to
*/
void connect([const] in NetAddrPtr aAddr);
[noscript] void connect([const] in NetAddrPtr aAddr);
/**
* Returns the local address of this UDP socket
@ -217,8 +217,8 @@ interface nsIUDPSocket : nsISupports
* @param addr The remote host address.
* @param stream The input stream to be sent. This must be a buffered stream implementation.
*/
void sendBinaryStreamWithAddress([const] in NetAddrPtr addr,
in nsIInputStream stream);
[noscript] void sendBinaryStreamWithAddress([const] in NetAddrPtr addr,
in nsIInputStream stream);
/**
* joinMulticast

View File

@ -32,7 +32,7 @@ native TypeResult(mozilla::net::TypeRecordResultType);
native MaybePort(mozilla::Maybe<uint16_t>);
native MaybeAlpnTuple(mozilla::Maybe<std::tuple<nsCString, mozilla::net::SupportedAlpnRank>>);
[scriptable, uuid(5d13241b-9d46-448a-90d8-77c418491026)]
[scriptable, builtinclass, uuid(5d13241b-9d46-448a-90d8-77c418491026)]
interface nsIDNSByTypeRecord : nsIDNSRecord
{
/**
@ -43,10 +43,10 @@ interface nsIDNSByTypeRecord : nsIDNSRecord
[noscript] readonly attribute TypeResult results;
};
[scriptable, uuid(2a71750d-cb21-45f1-9e1c-666d18dd7645)]
[scriptable, builtinclass, uuid(2a71750d-cb21-45f1-9e1c-666d18dd7645)]
interface nsIDNSTXTRecord : nsISupports
{
CStringArrayRef getRecords();
[noscript] CStringArrayRef getRecords();
/*
* Return concatenated strings.

View File

@ -26,12 +26,12 @@ interface nsINetAddr;
* like an enumerator, allowing the caller to easily step through the
* list of IP addresses.
*/
[scriptable, uuid(f92228ae-c417-4188-a604-0830a95e7eb9)]
[scriptable, builtinclass, uuid(f92228ae-c417-4188-a604-0830a95e7eb9)]
interface nsIDNSRecord : nsISupports
{
};
[scriptable, uuid(cb260e20-943f-4309-953b-78c90d3a7638)]
[scriptable, builtinclass, uuid(cb260e20-943f-4309-953b-78c90d3a7638)]
interface nsIDNSAddrRecord : nsIDNSRecord
{
/**

View File

@ -56,7 +56,7 @@ interface nsIHttpUpgradeListener : nsISupports
[must_use] void onUpgradeFailed(in nsresult aErrorCode);
void onWebSocketConnectionAvailable(in WebSocketConnectionBase aConnection);
[noscript] void onWebSocketConnectionAvailable(in WebSocketConnectionBase aConnection);
};
/**

View File

@ -146,7 +146,7 @@ interface nsIWebSocketChannel : nsISupports
in nsIWebSocketListener aListener,
in nsISupports aContext);
[must_use]
[must_use, noscript]
void asyncOpenNative(in nsIURI aURI,
in ACString aOrigin,
in OriginAttributes aOriginAttributes,

View File

@ -44,6 +44,7 @@ interface nsIWebTransport : nsISupports {
in unsigned long aSecurityFlags,
in WebTransportSessionEventListener aListener);
[noscript]
void asyncConnectWithClient(in nsIURI aURI,
in boolean aDedicated,
in Array<nsIWebTransportHash> aServerCertHashes,

View File

@ -12,7 +12,7 @@
* This is the callback interface for nsINamedPipeService.
* The functions are called by the internal thread in the nsINamedPipeService.
*/
[scriptable, uuid(de4f460b-94fd-442c-9002-1637beb2185a)]
[uuid(de4f460b-94fd-442c-9002-1637beb2185a)]
interface nsINamedPipeDataObserver : nsISupports
{
/**
@ -41,7 +41,7 @@ interface nsINamedPipeDataObserver : nsISupports
/**
* nsINamedPipeService
*/
[scriptable, uuid(1bf19133-5625-4ac8-836a-80b1c215f72b)]
[uuid(1bf19133-5625-4ac8-836a-80b1c215f72b)]
interface nsINamedPipeService : nsISupports
{
/**

View File

@ -15,7 +15,7 @@ interface nsIDocShellTreeItem;
* containing an embedded Gecko web browser.
*/
[scriptable, uuid(E8C414C4-DC38-4BA3-AB4E-EC4CBBE22907)]
[scriptable, builtinclass, uuid(E8C414C4-DC38-4BA3-AB4E-EC4CBBE22907)]
interface nsIWebBrowserChrome : nsISupports
{
/**
@ -124,7 +124,7 @@ interface nsIWebBrowserChrome : nsISupports
*
* @see nsIBaseWindow
*/
void setDimensions(in DimensionRequest aRequest);
[noscript] void setDimensions(in DimensionRequest aRequest);
/**
* Gets the dimensions of the window. The caller may pass
@ -134,7 +134,7 @@ interface nsIWebBrowserChrome : nsISupports
*
* @see nsIBaseWindow
*/
void getDimensions(in DimensionKind aDimensionKind, out long aX, out long aY, out long aCX, out long aCY);
[noscript] void getDimensions(in DimensionKind aDimensionKind, out long aX, out long aY, out long aCX, out long aCY);
/**
* Blur the window. This should unfocus the window and send an onblur event.

View File

@ -33,7 +33,7 @@ native nsDocShellLoadStatePtr(nsDocShellLoadState*);
* or the provider does not provide a window, the window watcher will proceed
* to actually open a new window.
*/
[scriptable, uuid(e97a3830-15ef-499b-8372-c22d128091c1)]
[scriptable, builtinclass, uuid(e97a3830-15ef-499b-8372-c22d128091c1)]
interface nsIWindowProvider : nsISupports
{
/**

View File

@ -161,7 +161,7 @@ interface nsIBaseWindow : nsISupports
*
* @see DimensionRequest
*/
void setDimensions(in DimensionRequest aRequest);
[noscript] void setDimensions(in DimensionRequest aRequest);
/**
* Gets the dimensions of the window. The caller may pass nullptr for any
@ -178,7 +178,7 @@ interface nsIBaseWindow : nsISupports
*
* @see DimensionRequest
*/
void getDimensions(in DimensionKind aDimensionKind, out long aX, out long aY, out long aCX, out long aCY);
[noscript] void getDimensions(in DimensionKind aDimensionKind, out long aX, out long aY, out long aCX, out long aCY);
/**
* Tell the window to repaint itself
@ -210,7 +210,7 @@ interface nsIBaseWindow : nsISupports
On controls that don't support setting nativeWindow parents, setting this
will return a NS_ERROR_NOT_IMPLEMENTED error.
*/
attribute nativeWindow parentNativeWindow;
[noscript] attribute nativeWindow parentNativeWindow;
/*
This is the handle (HWND, GdkWindow*, ...) to the native window of the

View File

@ -126,7 +126,7 @@ interface nsIDragService : nsISupports
*
* Note: This method is deprecated for non-native code.
*/
[can_run_script]
[noscript, can_run_script]
void invokeDragSessionWithSelection(in Selection aSelection,
in nsIPrincipal aPrincipal,
in nsIContentSecurityPolicy aCsp,

View File

@ -75,7 +75,7 @@ interface nsIScreen : nsISupports
/**
* ScreenColorGamut is native type, which cannot be declared [infallible].
*/
readonly attribute ScreenColorGamut colorGamut;
[noscript] readonly attribute ScreenColorGamut colorGamut;
/**
* The number of device pixels per desktop pixel for this screen (for

View File

@ -1216,19 +1216,24 @@ def ensureInfallibleIsSound(methodOrAttribute):
if methodOrAttribute.notxpcom:
raise IDLError(
"[infallible] does not make sense for a [notxpcom] " "method or attribute",
"[infallible] does not make sense for a [notxpcom] method or attribute",
methodOrAttribute.location,
)
# An interface cannot be implemented by JS if it has a notxpcom or nostdcall
# method or attribute, so it must be marked as builtinclass.
# method or attribute, or uses a by-value native type, so it must be marked as
# builtinclass.
def ensureBuiltinClassIfNeeded(methodOrAttribute):
iface = methodOrAttribute.iface
if not iface.attributes.scriptable or iface.attributes.builtinclass:
return
if iface.name == "nsISupports":
return
# notxpcom and nostdcall types change calling conventions, which breaks
# xptcall wrappers. We cannot allow XPCWrappedJS to be created for
# interfaces with these methods.
if methodOrAttribute.notxpcom:
raise IDLError(
(
@ -1248,6 +1253,84 @@ def ensureBuiltinClassIfNeeded(methodOrAttribute):
methodOrAttribute.location,
)
# Methods with custom native parameters passed without indirection cannot be
# safely handled by xptcall (as it cannot know the calling stack/register
# layout), so require the interface to be builtinclass.
#
# Only "in" parameters and writable attributes are checked, as other
# parameters are always passed indirectly, so do not impact calling
# conventions.
def typeNeedsBuiltinclass(type):
inner = type
while inner.kind == "typedef":
inner = inner.realtype
return (
inner.kind == "native"
and inner.specialtype is None
and inner.modifier is None
)
if methodOrAttribute.kind == "method":
for p in methodOrAttribute.params:
if p.paramtype == "in" and typeNeedsBuiltinclass(p.realtype):
raise IDLError(
(
"scriptable interface '%s' must be marked [builtinclass] "
"because it contains method '%s' with a by-value custom native "
"parameter '%s'"
)
% (iface.name, methodOrAttribute.name, p.name),
methodOrAttribute.location,
)
elif methodOrAttribute.kind == "attribute" and not methodOrAttribute.readonly:
if typeNeedsBuiltinclass(methodOrAttribute.realtype):
raise IDLError(
(
"scriptable interface '%s' must be marked [builtinclass] because it "
"contains writable attribute '%s' with a by-value custom native type"
)
% (iface.name, methodOrAttribute.name),
methodOrAttribute.location,
)
def ensureNoscriptIfNeeded(methodOrAttribute):
if not methodOrAttribute.isScriptable():
return
# NOTE: We can't check forward-declared interfaces to see if they're
# scriptable, as the information about whether they're scriptable is not
# known here.
def typeNeedsNoscript(type):
if type.kind in ["array", "legacyarray"]:
return typeNeedsNoscript(type.type)
if type.kind == "typedef":
return typeNeedsNoscript(type.realtype)
if type.kind == "native":
return type.specialtype is None
if type.kind == "interface":
return not type.attributes.scriptable
return False
if typeNeedsNoscript(methodOrAttribute.realtype):
raise IDLError(
"%s '%s' must be marked [noscript] because it has a non-scriptable type"
% (methodOrAttribute.kind, methodOrAttribute.name),
methodOrAttribute.location,
)
if methodOrAttribute.kind == "method":
for p in methodOrAttribute.params:
# iid_is arguments have their type ignored, so shouldn't be checked.
if not p.iid_is and typeNeedsNoscript(p.realtype):
raise IDLError(
(
"method '%s' must be marked [noscript] because it has a "
"non-scriptable parameter '%s'"
)
% (methodOrAttribute.name, p.name),
methodOrAttribute.location,
)
class Attribute(object):
kind = "attribute"
@ -1336,6 +1419,7 @@ class Attribute(object):
ensureInfallibleIsSound(self)
ensureBuiltinClassIfNeeded(self)
ensureNoscriptIfNeeded(self)
def toIDL(self):
attribs = attlistToIDL(self.attlist)
@ -1422,9 +1506,6 @@ class Method(object):
self.iface = iface
self.realtype = self.iface.idl.getName(self.type, self.location)
ensureInfallibleIsSound(self)
ensureBuiltinClassIfNeeded(self)
for p in self.params:
p.resolve(self)
for p in self.params:
@ -1464,6 +1545,10 @@ class Method(object):
self.location,
)
ensureInfallibleIsSound(self)
ensureBuiltinClassIfNeeded(self)
ensureNoscriptIfNeeded(self)
def isScriptable(self):
if not self.iface.attributes.scriptable:
return False

View File

@ -12,7 +12,7 @@ interface nsIInputStream;
* This allows reading unicode strings from a stream, automatically converting
* the bytes from a selected character encoding.
*/
[scriptable, uuid(FC66FFB6-5404-4908-A4A3-27F92FA0579D)]
[scriptable, builtinclass, uuid(FC66FFB6-5404-4908-A4A3-27F92FA0579D)]
interface nsIConverterInputStream : nsIUnicharInputStream {
/**
* Default replacement char value, U+FFFD REPLACEMENT CHARACTER.

View File

@ -42,7 +42,7 @@ native nsWriteUnicharSegmentFun(nsWriteUnicharSegmentFun);
* Abstract UTF-16 input stream
* @see nsIInputStream
*/
[scriptable, uuid(d5e3bd80-6723-4b92-b0c9-22f6162fd94f)]
[scriptable, builtinclass, uuid(d5e3bd80-6723-4b92-b0c9-22f6162fd94f)]
interface nsIUnicharInputStream : nsISupports {
/**
* Reads into a caller-provided array.

View File

@ -330,30 +330,11 @@ def link_to_cpp(interfaces, fd, header_fd):
)
)
def is_type_reflectable(type):
# All native types end up getting tagged as void*, or as wrapper types around void*
if type["tag"] == "TD_VOID":
return False
if type["tag"] in ("TD_ARRAY", "TD_LEGACY_ARRAY"):
return is_type_reflectable(type["element"])
return True
def is_method_reflectable(method):
if "hidden" in method["flags"]:
return False
for param in method["params"]:
# Reflected methods can't use non-reflectable types.
if not is_type_reflectable(param["type"]):
return False
return True
def lower_method(method, ifacename, builtinclass):
methodname = "%s::%s" % (ifacename, method["name"])
isSymbol = "symbol" in method["flags"]
reflectable = is_method_reflectable(method)
reflectable = "hidden" not in method["flags"]
if not reflectable and builtinclass:
# Hide the parameters of methods that can't be called from JS and

View File

@ -29,7 +29,7 @@ interface nsINestedEventLoopCondition : nsISupports
/**
* An interface for creating and locating nsIThread instances.
*/
[scriptable, uuid(1be89eca-e2f7-453b-8d38-c11ba247f6f3)]
[scriptable, builtinclass, uuid(1be89eca-e2f7-453b-8d38-c11ba247f6f3)]
interface nsIThreadManager : nsISupports
{
/**