js/xpconnect/src/xpcpublic.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/xpconnect/src/xpcpublic.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,521 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=99: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#ifndef xpcpublic_h
    1.11 +#define xpcpublic_h
    1.12 +
    1.13 +#include "jsapi.h"
    1.14 +#include "jsproxy.h"
    1.15 +#include "js/HeapAPI.h"
    1.16 +#include "js/GCAPI.h"
    1.17 +
    1.18 +#include "nsISupports.h"
    1.19 +#include "nsIURI.h"
    1.20 +#include "nsIPrincipal.h"
    1.21 +#include "nsWrapperCache.h"
    1.22 +#include "nsStringGlue.h"
    1.23 +#include "nsTArray.h"
    1.24 +#include "mozilla/dom/JSSlots.h"
    1.25 +#include "nsMathUtils.h"
    1.26 +#include "nsStringBuffer.h"
    1.27 +#include "mozilla/dom/BindingDeclarations.h"
    1.28 +
    1.29 +class nsGlobalWindow;
    1.30 +class nsIPrincipal;
    1.31 +class nsScriptNameSpaceManager;
    1.32 +class nsIGlobalObject;
    1.33 +class nsIMemoryReporterCallback;
    1.34 +
    1.35 +#ifndef BAD_TLS_INDEX
    1.36 +#define BAD_TLS_INDEX ((uint32_t) -1)
    1.37 +#endif
    1.38 +
    1.39 +namespace xpc {
    1.40 +
    1.41 +class Scriptability {
    1.42 +public:
    1.43 +    Scriptability(JSCompartment *c);
    1.44 +    bool Allowed();
    1.45 +    bool IsImmuneToScriptPolicy();
    1.46 +
    1.47 +    void Block();
    1.48 +    void Unblock();
    1.49 +    void SetDocShellAllowsScript(bool aAllowed);
    1.50 +
    1.51 +    static Scriptability& Get(JSObject *aScope);
    1.52 +
    1.53 +private:
    1.54 +    // Whenever a consumer wishes to prevent script from running on a global,
    1.55 +    // it increments this value with a call to Block(). When it wishes to
    1.56 +    // re-enable it (if ever), it decrements this value with a call to Unblock().
    1.57 +    // Script may not run if this value is non-zero.
    1.58 +    uint32_t mScriptBlocks;
    1.59 +
    1.60 +    // Whether the docshell allows javascript in this scope. If this scope
    1.61 +    // doesn't have a docshell, this value is always true.
    1.62 +    bool mDocShellAllowsScript;
    1.63 +
    1.64 +    // Whether this scope is immune to user-defined or addon-defined script
    1.65 +    // policy.
    1.66 +    bool mImmuneToScriptPolicy;
    1.67 +
    1.68 +    // Whether the new-style domain policy when this compartment was created
    1.69 +    // forbids script execution.
    1.70 +    bool mScriptBlockedByPolicy;
    1.71 +};
    1.72 +
    1.73 +JSObject *
    1.74 +TransplantObject(JSContext *cx, JS::HandleObject origobj, JS::HandleObject target);
    1.75 +
    1.76 +bool IsXBLScope(JSCompartment *compartment);
    1.77 +bool IsInXBLScope(JSObject *obj);
    1.78 +
    1.79 +// Return a raw XBL scope object corresponding to contentScope, which must
    1.80 +// be an object whose global is a DOM window.
    1.81 +//
    1.82 +// The return value is not wrapped into cx->compartment, so be sure to enter
    1.83 +// its compartment before doing anything meaningful.
    1.84 +//
    1.85 +// Also note that XBL scopes are lazily created, so the return-value should be
    1.86 +// null-checked unless the caller can ensure that the scope must already
    1.87 +// exist.
    1.88 +//
    1.89 +// This function asserts if |contentScope| is itself in an XBL scope to catch
    1.90 +// sloppy consumers. Conversely, GetXBLScopeOrGlobal will handle objects that
    1.91 +// are in XBL scope (by just returning the global).
    1.92 +JSObject *
    1.93 +GetXBLScope(JSContext *cx, JSObject *contentScope);
    1.94 +
    1.95 +inline JSObject *
    1.96 +GetXBLScopeOrGlobal(JSContext *cx, JSObject *obj) {
    1.97 +    if (IsInXBLScope(obj))
    1.98 +        return js::GetGlobalForObjectCrossCompartment(obj);
    1.99 +    return GetXBLScope(cx, obj);
   1.100 +}
   1.101 +
   1.102 +// Returns whether XBL scopes have been explicitly disabled for code running
   1.103 +// in this compartment. See the comment around mAllowXBLScope.
   1.104 +bool
   1.105 +AllowXBLScope(JSCompartment *c);
   1.106 +
   1.107 +// Returns whether we will use an XBL scope for this compartment. This is
   1.108 +// semantically equivalent to comparing global != GetXBLScope(global), but it
   1.109 +// does not have the side-effect of eagerly creating the XBL scope if it does
   1.110 +// not already exist.
   1.111 +bool
   1.112 +UseXBLScope(JSCompartment *c);
   1.113 +
   1.114 +bool
   1.115 +IsSandboxPrototypeProxy(JSObject *obj);
   1.116 +
   1.117 +bool
   1.118 +IsReflector(JSObject *obj);
   1.119 +
   1.120 +bool
   1.121 +IsXrayWrapper(JSObject *obj);
   1.122 +
   1.123 +// If this function was created for a given XrayWrapper, returns the global of
   1.124 +// the Xrayed object. Otherwise, returns the global of the function.
   1.125 +//
   1.126 +// To emphasize the obvious: the return value here is not necessarily same-
   1.127 +// compartment with the argument.
   1.128 +JSObject *
   1.129 +XrayAwareCalleeGlobal(JSObject *fun);
   1.130 +
   1.131 +void
   1.132 +TraceXPCGlobal(JSTracer *trc, JSObject *obj);
   1.133 +
   1.134 +} /* namespace xpc */
   1.135 +
   1.136 +namespace JS {
   1.137 +
   1.138 +struct RuntimeStats;
   1.139 +
   1.140 +}
   1.141 +
   1.142 +#define XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(n)                            \
   1.143 +    JSCLASS_DOM_GLOBAL | JSCLASS_HAS_PRIVATE |                                \
   1.144 +    JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_IMPLEMENTS_BARRIERS |            \
   1.145 +    JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS + n)
   1.146 +
   1.147 +#define XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET (JSCLASS_GLOBAL_SLOT_COUNT + DOM_GLOBAL_SLOTS)
   1.148 +
   1.149 +#define XPCONNECT_GLOBAL_FLAGS XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(0)
   1.150 +
   1.151 +inline JSObject*
   1.152 +xpc_FastGetCachedWrapper(JSContext *cx, nsWrapperCache *cache, JS::MutableHandleValue vp)
   1.153 +{
   1.154 +    if (cache) {
   1.155 +        JSObject* wrapper = cache->GetWrapper();
   1.156 +        if (wrapper &&
   1.157 +            js::GetObjectCompartment(wrapper) == js::GetContextCompartment(cx))
   1.158 +        {
   1.159 +            vp.setObject(*wrapper);
   1.160 +            return wrapper;
   1.161 +        }
   1.162 +    }
   1.163 +
   1.164 +    return nullptr;
   1.165 +}
   1.166 +
   1.167 +// The JS GC marks objects gray that are held alive directly or
   1.168 +// indirectly by an XPConnect root. The cycle collector explores only
   1.169 +// this subset of the JS heap.
   1.170 +inline bool
   1.171 +xpc_IsGrayGCThing(void *thing)
   1.172 +{
   1.173 +    return JS::GCThingIsMarkedGray(thing);
   1.174 +}
   1.175 +
   1.176 +// The cycle collector only cares about some kinds of GCthings that are
   1.177 +// reachable from an XPConnect root. Implemented in nsXPConnect.cpp.
   1.178 +extern bool
   1.179 +xpc_GCThingIsGrayCCThing(void *thing);
   1.180 +
   1.181 +inline JSScript *
   1.182 +xpc_UnmarkGrayScript(JSScript *script)
   1.183 +{
   1.184 +    if (script)
   1.185 +        JS::ExposeGCThingToActiveJS(script, JSTRACE_SCRIPT);
   1.186 +
   1.187 +    return script;
   1.188 +}
   1.189 +
   1.190 +// If aVariant is an XPCVariant, this marks the object to be in aGeneration.
   1.191 +// This also unmarks the gray JSObject.
   1.192 +extern void
   1.193 +xpc_MarkInCCGeneration(nsISupports* aVariant, uint32_t aGeneration);
   1.194 +
   1.195 +// If aWrappedJS is a JS wrapper, unmark its JSObject.
   1.196 +extern void
   1.197 +xpc_TryUnmarkWrappedGrayObject(nsISupports* aWrappedJS);
   1.198 +
   1.199 +extern void
   1.200 +xpc_UnmarkSkippableJSHolders();
   1.201 +
   1.202 +// No JS can be on the stack when this is called. Probably only useful from
   1.203 +// xpcshell.
   1.204 +void
   1.205 +xpc_ActivateDebugMode();
   1.206 +
   1.207 +// readable string conversions, static methods and members only
   1.208 +class XPCStringConvert
   1.209 +{
   1.210 +    // One-slot cache, because it turns out it's common for web pages to
   1.211 +    // get the same string a few times in a row.  We get about a 40% cache
   1.212 +    // hit rate on this cache last it was measured.  We'd get about 70%
   1.213 +    // hit rate with a hashtable with removal on finalization, but that
   1.214 +    // would take a lot more machinery.
   1.215 +    struct ZoneStringCache
   1.216 +    {
   1.217 +        nsStringBuffer* mBuffer;
   1.218 +        JSString* mString;
   1.219 +    };
   1.220 +
   1.221 +public:
   1.222 +
   1.223 +    // If the string shares the readable's buffer, that buffer will
   1.224 +    // get assigned to *sharedBuffer.  Otherwise null will be
   1.225 +    // assigned.
   1.226 +    static bool ReadableToJSVal(JSContext *cx, const nsAString &readable,
   1.227 +                                nsStringBuffer** sharedBuffer,
   1.228 +                                JS::MutableHandleValue vp);
   1.229 +
   1.230 +    // Convert the given stringbuffer/length pair to a jsval
   1.231 +    static MOZ_ALWAYS_INLINE bool
   1.232 +    StringBufferToJSVal(JSContext* cx, nsStringBuffer* buf, uint32_t length,
   1.233 +                        JS::MutableHandleValue rval, bool* sharedBuffer)
   1.234 +    {
   1.235 +        JS::Zone *zone = js::GetContextZone(cx);
   1.236 +        ZoneStringCache *cache = static_cast<ZoneStringCache*>(JS_GetZoneUserData(zone));
   1.237 +        if (cache && buf == cache->mBuffer) {
   1.238 +            MOZ_ASSERT(JS::GetGCThingZone(cache->mString) == zone);
   1.239 +            JS::MarkStringAsLive(zone, cache->mString);
   1.240 +            rval.setString(cache->mString);
   1.241 +            *sharedBuffer = false;
   1.242 +            return true;
   1.243 +        }
   1.244 +
   1.245 +        JSString *str = JS_NewExternalString(cx,
   1.246 +                                             static_cast<jschar*>(buf->Data()),
   1.247 +                                             length, &sDOMStringFinalizer);
   1.248 +        if (!str) {
   1.249 +            return false;
   1.250 +        }
   1.251 +        rval.setString(str);
   1.252 +        if (!cache) {
   1.253 +            cache = new ZoneStringCache();
   1.254 +            JS_SetZoneUserData(zone, cache);
   1.255 +        }
   1.256 +        cache->mBuffer = buf;
   1.257 +        cache->mString = str;
   1.258 +        *sharedBuffer = true;
   1.259 +        return true;
   1.260 +    }
   1.261 +
   1.262 +    static void FreeZoneCache(JS::Zone *zone);
   1.263 +    static void ClearZoneCache(JS::Zone *zone);
   1.264 +
   1.265 +    static MOZ_ALWAYS_INLINE bool IsLiteral(JSString *str)
   1.266 +    {
   1.267 +        return JS_IsExternalString(str) &&
   1.268 +               JS_GetExternalStringFinalizer(str) == &sLiteralFinalizer;
   1.269 +    }
   1.270 +
   1.271 +    static MOZ_ALWAYS_INLINE bool IsDOMString(JSString *str)
   1.272 +    {
   1.273 +        return JS_IsExternalString(str) &&
   1.274 +               JS_GetExternalStringFinalizer(str) == &sDOMStringFinalizer;
   1.275 +    }
   1.276 +
   1.277 +private:
   1.278 +    static const JSStringFinalizer sLiteralFinalizer, sDOMStringFinalizer;
   1.279 +
   1.280 +    static void FinalizeLiteral(const JSStringFinalizer *fin, jschar *chars);
   1.281 +
   1.282 +    static void FinalizeDOMString(const JSStringFinalizer *fin, jschar *chars);
   1.283 +
   1.284 +    XPCStringConvert();         // not implemented
   1.285 +};
   1.286 +
   1.287 +namespace xpc {
   1.288 +
   1.289 +// If these functions return false, then an exception will be set on cx.
   1.290 +bool Base64Encode(JSContext *cx, JS::HandleValue val, JS::MutableHandleValue out);
   1.291 +bool Base64Decode(JSContext *cx, JS::HandleValue val, JS::MutableHandleValue out);
   1.292 +
   1.293 +/**
   1.294 + * Convert an nsString to jsval, returning true on success.
   1.295 + * Note, the ownership of the string buffer may be moved from str to rval.
   1.296 + * If that happens, str will point to an empty string after this call.
   1.297 + */
   1.298 +bool NonVoidStringToJsval(JSContext *cx, nsAString &str, JS::MutableHandleValue rval);
   1.299 +inline bool StringToJsval(JSContext *cx, nsAString &str, JS::MutableHandleValue rval)
   1.300 +{
   1.301 +    // From the T_DOMSTRING case in XPCConvert::NativeData2JS.
   1.302 +    if (str.IsVoid()) {
   1.303 +        rval.setNull();
   1.304 +        return true;
   1.305 +    }
   1.306 +    return NonVoidStringToJsval(cx, str, rval);
   1.307 +}
   1.308 +
   1.309 +inline bool
   1.310 +NonVoidStringToJsval(JSContext* cx, const nsAString& str, JS::MutableHandleValue rval)
   1.311 +{
   1.312 +    nsString mutableCopy(str);
   1.313 +    return NonVoidStringToJsval(cx, mutableCopy, rval);
   1.314 +}
   1.315 +
   1.316 +inline bool
   1.317 +StringToJsval(JSContext* cx, const nsAString& str, JS::MutableHandleValue rval)
   1.318 +{
   1.319 +    nsString mutableCopy(str);
   1.320 +    return StringToJsval(cx, mutableCopy, rval);
   1.321 +}
   1.322 +
   1.323 +/**
   1.324 + * As above, but for mozilla::dom::DOMString.
   1.325 + */
   1.326 +MOZ_ALWAYS_INLINE
   1.327 +bool NonVoidStringToJsval(JSContext* cx, mozilla::dom::DOMString& str,
   1.328 +                          JS::MutableHandleValue rval)
   1.329 +{
   1.330 +    if (!str.HasStringBuffer()) {
   1.331 +        // It's an actual XPCOM string
   1.332 +        return NonVoidStringToJsval(cx, str.AsAString(), rval);
   1.333 +    }
   1.334 +
   1.335 +    uint32_t length = str.StringBufferLength();
   1.336 +    if (length == 0) {
   1.337 +        rval.set(JS_GetEmptyStringValue(cx));
   1.338 +        return true;
   1.339 +    }
   1.340 +
   1.341 +    nsStringBuffer* buf = str.StringBuffer();
   1.342 +    bool shared;
   1.343 +    if (!XPCStringConvert::StringBufferToJSVal(cx, buf, length, rval,
   1.344 +                                               &shared)) {
   1.345 +        return false;
   1.346 +    }
   1.347 +    if (shared) {
   1.348 +        // JS now needs to hold a reference to the buffer
   1.349 +        buf->AddRef();
   1.350 +    }
   1.351 +    return true;
   1.352 +}
   1.353 +
   1.354 +MOZ_ALWAYS_INLINE
   1.355 +bool StringToJsval(JSContext* cx, mozilla::dom::DOMString& str,
   1.356 +                   JS::MutableHandleValue rval)
   1.357 +{
   1.358 +    if (str.IsNull()) {
   1.359 +        rval.setNull();
   1.360 +        return true;
   1.361 +    }
   1.362 +    return NonVoidStringToJsval(cx, str, rval);
   1.363 +}
   1.364 +
   1.365 +nsIPrincipal *GetCompartmentPrincipal(JSCompartment *compartment);
   1.366 +
   1.367 +void SetLocationForGlobal(JSObject *global, const nsACString& location);
   1.368 +void SetLocationForGlobal(JSObject *global, nsIURI *locationURI);
   1.369 +
   1.370 +// ReportJSRuntimeExplicitTreeStats will expect this in the |extra| member
   1.371 +// of JS::ZoneStats.
   1.372 +class ZoneStatsExtras {
   1.373 +public:
   1.374 +    ZoneStatsExtras()
   1.375 +    {}
   1.376 +
   1.377 +    nsAutoCString pathPrefix;
   1.378 +
   1.379 +private:
   1.380 +    ZoneStatsExtras(const ZoneStatsExtras &other) MOZ_DELETE;
   1.381 +    ZoneStatsExtras& operator=(const ZoneStatsExtras &other) MOZ_DELETE;
   1.382 +};
   1.383 +
   1.384 +// ReportJSRuntimeExplicitTreeStats will expect this in the |extra| member
   1.385 +// of JS::CompartmentStats.
   1.386 +class CompartmentStatsExtras {
   1.387 +public:
   1.388 +    CompartmentStatsExtras()
   1.389 +    {}
   1.390 +
   1.391 +    nsAutoCString jsPathPrefix;
   1.392 +    nsAutoCString domPathPrefix;
   1.393 +    nsCOMPtr<nsIURI> location;
   1.394 +
   1.395 +private:
   1.396 +    CompartmentStatsExtras(const CompartmentStatsExtras &other) MOZ_DELETE;
   1.397 +    CompartmentStatsExtras& operator=(const CompartmentStatsExtras &other) MOZ_DELETE;
   1.398 +};
   1.399 +
   1.400 +// This reports all the stats in |rtStats| that belong in the "explicit" tree,
   1.401 +// (which isn't all of them).
   1.402 +// @see ZoneStatsExtras
   1.403 +// @see CompartmentStatsExtras
   1.404 +nsresult
   1.405 +ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
   1.406 +                                 const nsACString &rtPath,
   1.407 +                                 nsIMemoryReporterCallback *cb,
   1.408 +                                 nsISupports *closure, size_t *rtTotal = nullptr);
   1.409 +
   1.410 +/**
   1.411 + * Throws an exception on cx and returns false.
   1.412 + */
   1.413 +bool
   1.414 +Throw(JSContext *cx, nsresult rv);
   1.415 +
   1.416 +/**
   1.417 + * Every global should hold a native that implements the nsIGlobalObject interface.
   1.418 + */
   1.419 +nsIGlobalObject *
   1.420 +GetNativeForGlobal(JSObject *global);
   1.421 +
   1.422 +/**
   1.423 + * In some cases a native object does not really belong to any compartment (XBL,
   1.424 + * document created from by XHR of a worker, etc.). But when for some reason we
   1.425 + * have to wrap these natives (because of an event for example) instead of just
   1.426 + * wrapping them into some random compartment we find on the context stack (like
   1.427 + * we did previously) a default compartment is used. This function returns that
   1.428 + * compartment's global. It is a singleton on the runtime.
   1.429 + * If you find yourself wanting to use this compartment, you're probably doing
   1.430 + * something wrong. Callers MUST consult with the XPConnect module owner before
   1.431 + * using this compartment. If you don't, bholley will hunt you down.
   1.432 + */
   1.433 +JSObject *
   1.434 +GetJunkScope();
   1.435 +
   1.436 +/**
   1.437 + * Returns the native global of the junk scope. See comment of GetJunkScope
   1.438 + * about the conditions of using it.
   1.439 + */
   1.440 +nsIGlobalObject *
   1.441 +GetJunkScopeGlobal();
   1.442 +
   1.443 +/**
   1.444 + * Shared compilation scope for XUL prototype documents and XBL
   1.445 + * precompilation. This compartment has a null principal. No code may run, and
   1.446 + * it is invisible to the debugger.
   1.447 + */
   1.448 +JSObject *
   1.449 +GetCompilationScope();
   1.450 +
   1.451 +/**
   1.452 + * If |aObj| is a window, returns the associated nsGlobalWindow.
   1.453 + * Otherwise, returns null.
   1.454 + */
   1.455 +nsGlobalWindow*
   1.456 +WindowOrNull(JSObject *aObj);
   1.457 +
   1.458 +/*
   1.459 + * Returns the dummy global associated with the SafeJSContext. Callers MUST
   1.460 + * consult with the XPConnect module owner before using this function.
   1.461 + */
   1.462 +JSObject *
   1.463 +GetSafeJSContextGlobal();
   1.464 +
   1.465 +/**
   1.466 + * If |aObj| has a window for a global, returns the associated nsGlobalWindow.
   1.467 + * Otherwise, returns null.
   1.468 + */
   1.469 +nsGlobalWindow*
   1.470 +WindowGlobalOrNull(JSObject *aObj);
   1.471 +
   1.472 +// Error reporter used when there is no associated DOM window on to which to
   1.473 +// report errors and warnings.
   1.474 +void
   1.475 +SystemErrorReporter(JSContext *cx, const char *message, JSErrorReport *rep);
   1.476 +
   1.477 +void
   1.478 +SimulateActivityCallback(bool aActive);
   1.479 +
   1.480 +void
   1.481 +RecordAdoptedNode(JSCompartment *c);
   1.482 +
   1.483 +void
   1.484 +RecordDonatedNode(JSCompartment *c);
   1.485 +
   1.486 +// This function may be used off-main-thread, in which case it is benignly
   1.487 +// racey.
   1.488 +bool
   1.489 +ShouldDiscardSystemSource();
   1.490 +
   1.491 +} // namespace xpc
   1.492 +
   1.493 +namespace mozilla {
   1.494 +namespace dom {
   1.495 +
   1.496 +typedef JSObject*
   1.497 +(*DefineInterface)(JSContext *cx, JS::Handle<JSObject*> global,
   1.498 +                   JS::Handle<jsid> id, bool defineOnGlobal);
   1.499 +
   1.500 +typedef JSObject*
   1.501 +(*ConstructNavigatorProperty)(JSContext *cx, JS::Handle<JSObject*> naviObj);
   1.502 +
   1.503 +// Check whether a constructor should be enabled for the given object.
   1.504 +// Note that the object should NOT be an Xray, since Xrays will end up
   1.505 +// defining constructors on the underlying object.
   1.506 +// This is a typedef for the function type itself, not the function
   1.507 +// pointer, so it's more obvious that pointers to a ConstructorEnabled
   1.508 +// can be null.
   1.509 +typedef bool
   1.510 +(ConstructorEnabled)(JSContext* cx, JS::Handle<JSObject*> obj);
   1.511 +
   1.512 +void
   1.513 +Register(nsScriptNameSpaceManager* aNameSpaceManager);
   1.514 +
   1.515 +/**
   1.516 + * A test for whether WebIDL methods that should only be visible to
   1.517 + * chrome or XBL scopes should be exposed.
   1.518 + */
   1.519 +bool IsChromeOrXBL(JSContext* cx, JSObject* /* unused */);
   1.520 +
   1.521 +} // namespace dom
   1.522 +} // namespace mozilla
   1.523 +
   1.524 +#endif

mercurial