mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 13:23:07 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1165162 - Rework the nsIScriptSecurityManager principal-minting API to be originAttributes-centric. r=gabor,r=bholley,sr=sicking (269536132) - pointer style and mispatch (38b32b3bc) - more misspatch & pointer style (ff2bc3057) - Bug 1164014 - Fixing defaultShims. r=billm (bcf7f671a) - Bug 1164014 - Workaround for GC bug 1172193. r=gkrizsanits (b25cf4dea) - Bug 1158427 - r=roc (64037cb2c) - Bug 1157994 - Ensure AudioContext operations are started and resolved in the same order. r=roc (757de0f81) - Bug 1127188 - Properly handle AudioContext.close() calls right after the creation of an AudioContext. r=ehsan (88362873f) - Bug 1164011 - interposition for CPOWS. r=billm (24a8134f6) - Bug 1164014 - Shim optimization. r=billm (9a8498684) - Bug 1178581 - Interning does not and should not imply infinite lifetime; r=sfink (91dfc5b77) - Bug 1171053 - Remove JS_BindCallable. r=efaust (2e59b8c62) - Bug 1174372 - Initialize ExecutableAllocator static fields in JS_Init. r=luke (d02620196) - missing uid of 968334 (3c73a17db) - missing uuid of 1152577 (13d58364c) - add missing uuid of 1050500 (16c61b629) - Bug 110567 - Remove nsIDocShell::GetURLSearchParams(), r=smaug (5018a0936) - Bug 1132518, add a flag to nsIFrameTraversal to skip the popup checks, r=mats (8482fd8fd) - Bug 1132518, make document navigation with F6/Shift+F6 work in e10s. This combines the document and tab navigation mechanisms together, r=smaug (2085e999b) - Bug 1160307 - Capture async stack frames on Javascript timeline markers. r=fitzgen, r=smaug, r=Paolo (95c3e6b95)
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* This list allows pre-existing or 'unfixable' JS issues to remain, while we
|
||||
* detect newly occurring issues in shipping JS. It is a list of regexes
|
||||
* matching files which have errors:
|
||||
*/
|
||||
const kWhitelist = new Set([
|
||||
/defaults\/profile\/prefs.js$/,
|
||||
/browser\/content\/browser\/places\/controller.js$/,
|
||||
]);
|
||||
|
||||
|
||||
let moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm");
|
||||
let {generateURIsFromDirTree} = Cu.import(moduleLocation, {});
|
||||
|
||||
// Normally we would use reflect.jsm to get Reflect.parse. However, if
|
||||
// we do that, then all the AST data is allocated in reflect.jsm's
|
||||
// zone. That exposes a bug in our GC. The GC collects reflect.jsm's
|
||||
// zone but not the zone in which our test code lives (since no new
|
||||
// data is being allocated in it). The cross-compartment wrappers in
|
||||
// our zone that point to the AST data never get collected, and so the
|
||||
// AST data itself is never collected. We need to GC both zones at
|
||||
// once to fix the problem.
|
||||
const init = Components.classes["@mozilla.org/jsreflect;1"].createInstance();
|
||||
init();
|
||||
|
||||
/**
|
||||
* Check if an error should be ignored due to matching one of the whitelist
|
||||
* objects defined in kWhitelist
|
||||
*
|
||||
* @param uri the uri to check against the whitelist
|
||||
* @return true if the uri should be skipped, false otherwise.
|
||||
*/
|
||||
function uriIsWhiteListed(uri) {
|
||||
for (let whitelistItem of kWhitelist) {
|
||||
if (whitelistItem.test(uri.spec)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function parsePromise(uri) {
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", uri, true);
|
||||
xhr.onreadystatechange = function() {
|
||||
if (this.readyState == this.DONE) {
|
||||
let scriptText = this.responseText;
|
||||
let ast;
|
||||
try {
|
||||
info("Checking " + uri);
|
||||
ast = Reflect.parse(scriptText);
|
||||
resolve(true);
|
||||
} catch (ex) {
|
||||
let errorMsg = "Script error reading " + uri + ": " + ex;
|
||||
ok(false, errorMsg);
|
||||
resolve(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.onerror = (error) => {
|
||||
ok(false, "XHR error reading " + uri + ": " + error);
|
||||
resolve(false);
|
||||
};
|
||||
xhr.overrideMimeType("application/javascript");
|
||||
xhr.send(null);
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
add_task(function* checkAllTheJS() {
|
||||
// In debug builds, even on a fast machine, collecting the file list may take
|
||||
// more than 30 seconds, and parsing all files may take four more minutes.
|
||||
// For this reason, this test must be explictly requested in debug builds by
|
||||
// using the "--setpref parse=<filter>" argument to mach. You can specify:
|
||||
// - A case-sensitive substring of the file name to test (slow).
|
||||
// - A single absolute URI printed out by a previous run (fast).
|
||||
// - An empty string to run the test on all files (slowest).
|
||||
let parseRequested = Services.prefs.prefHasUserValue("parse");
|
||||
let parseValue = parseRequested && Services.prefs.getCharPref("parse");
|
||||
if (SpecialPowers.isDebugBuild) {
|
||||
if (!parseRequested) {
|
||||
ok(true, "Test disabled on debug build. To run, execute: ./mach" +
|
||||
" mochitest-browser --setpref parse=<case_sensitive_filter>" +
|
||||
" browser/base/content/test/general/browser_parsable_script.js");
|
||||
return;
|
||||
}
|
||||
// Request a 15 minutes timeout (30 seconds * 30) for debug builds.
|
||||
requestLongerTimeout(30);
|
||||
}
|
||||
|
||||
let uris;
|
||||
// If an absolute URI is specified on the command line, use it immediately.
|
||||
if (parseValue && parseValue.includes(":")) {
|
||||
uris = [NetUtil.newURI(parseValue)];
|
||||
} else {
|
||||
let appDir = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
|
||||
// This asynchronously produces a list of URLs (sadly, mostly sync on our
|
||||
// test infrastructure because it runs against jarfiles there, and
|
||||
// our zipreader APIs are all sync)
|
||||
let startTimeMs = Date.now();
|
||||
info("Collecting URIs");
|
||||
uris = yield generateURIsFromDirTree(appDir, [".js", ".jsm"]);
|
||||
info("Collected URIs in " + (Date.now() - startTimeMs) + "ms");
|
||||
|
||||
// Apply the filter specified on the command line, if any.
|
||||
if (parseValue) {
|
||||
uris = uris.filter(uri => {
|
||||
if (uri.spec.includes(parseValue)) {
|
||||
return true;
|
||||
}
|
||||
info("Not checking filtered out " + uri.spec);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// We create an array of promises so we can parallelize all our parsing
|
||||
// and file loading activity:
|
||||
let allPromises = [];
|
||||
for (let uri of uris) {
|
||||
if (uriIsWhiteListed(uri)) {
|
||||
info("Not checking whitelisted " + uri.spec);
|
||||
continue;
|
||||
}
|
||||
allPromises.push(parsePromise(uri.spec));
|
||||
}
|
||||
|
||||
let promiseResults = yield Promise.all(allPromises);
|
||||
is(promiseResults.filter((x) => !x).length, 0, "There should be 0 parsing errors");
|
||||
});
|
||||
|
||||
@@ -448,6 +448,7 @@
|
||||
@RESPATH@/components/nsUpdateTimerManager.js
|
||||
@RESPATH@/components/addoncompat.manifest
|
||||
@RESPATH@/components/multiprocessShims.js
|
||||
@RESPATH@/components/defaultShims.js
|
||||
@RESPATH@/components/remoteTagService.js
|
||||
@RESPATH@/components/pluginGlue.manifest
|
||||
@RESPATH@/components/ProcessSingleton.manifest
|
||||
|
||||
+39
-3
@@ -8,6 +8,10 @@
|
||||
|
||||
#include "nsIObjectInputStream.h"
|
||||
#include "nsIObjectOutputStream.h"
|
||||
|
||||
#include "nsPrincipal.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsNullPrincipal.h"
|
||||
#include "nsScriptSecurityManager.h"
|
||||
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
@@ -15,7 +19,7 @@
|
||||
namespace mozilla {
|
||||
|
||||
void
|
||||
BasePrincipal::OriginAttributes::CreateSuffix(nsACString& aStr)
|
||||
OriginAttributes::CreateSuffix(nsACString& aStr)
|
||||
{
|
||||
aStr.Truncate();
|
||||
MOZ_RELEASE_ASSERT(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
|
||||
@@ -32,14 +36,14 @@ BasePrincipal::OriginAttributes::CreateSuffix(nsACString& aStr)
|
||||
}
|
||||
|
||||
void
|
||||
BasePrincipal::OriginAttributes::Serialize(nsIObjectOutputStream* aStream) const
|
||||
OriginAttributes::Serialize(nsIObjectOutputStream* aStream) const
|
||||
{
|
||||
aStream->Write32(mAppId);
|
||||
aStream->WriteBoolean(mInBrowser);
|
||||
}
|
||||
|
||||
nsresult
|
||||
BasePrincipal::OriginAttributes::Deserialize(nsIObjectInputStream* aStream)
|
||||
OriginAttributes::Deserialize(nsIObjectInputStream* aStream)
|
||||
{
|
||||
nsresult rv = aStream->Read32(&mAppId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@@ -179,4 +183,36 @@ BasePrincipal::GetUnknownAppId(bool* aUnknownAppId)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<BasePrincipal>
|
||||
BasePrincipal::CreateCodebasePrincipal(nsIURI* aURI, OriginAttributes& aAttrs)
|
||||
{
|
||||
// If the URI is supposed to inherit the security context of whoever loads it,
|
||||
// we shouldn't make a codebase principal for it.
|
||||
bool inheritsPrincipal;
|
||||
nsresult rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
|
||||
&inheritsPrincipal);
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
if (NS_FAILED(rv) || inheritsPrincipal) {
|
||||
return nsNullPrincipal::Create();
|
||||
}
|
||||
|
||||
// Check whether the URI knows what its principal is supposed to be.
|
||||
nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(aURI);
|
||||
if (uriPrinc) {
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
uriPrinc->GetPrincipal(getter_AddRefs(principal));
|
||||
if (!principal) {
|
||||
return nsNullPrincipal::Create();
|
||||
}
|
||||
nsRefPtr<BasePrincipal> concrete = Cast(principal);
|
||||
return concrete.forget();
|
||||
}
|
||||
|
||||
// Mint a codebase principal.
|
||||
nsRefPtr<nsPrincipal> codebase = new nsPrincipal();
|
||||
rv = codebase->Init(aURI, aAttrs);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
return codebase.forget();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
+30
-27
@@ -18,6 +18,35 @@ class nsIObjectInputStream;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class OriginAttributes : public dom::OriginAttributesDictionary
|
||||
{
|
||||
public:
|
||||
OriginAttributes() {}
|
||||
OriginAttributes(uint32_t aAppId, bool aInBrowser)
|
||||
{
|
||||
mAppId = aAppId;
|
||||
mInBrowser = aInBrowser;
|
||||
}
|
||||
|
||||
bool operator==(const OriginAttributes& aOther) const
|
||||
{
|
||||
return mAppId == aOther.mAppId &&
|
||||
mInBrowser == aOther.mInBrowser;
|
||||
}
|
||||
bool operator!=(const OriginAttributes& aOther) const
|
||||
{
|
||||
return !(*this == aOther);
|
||||
}
|
||||
|
||||
// Serializes non-default values into the suffix format, i.e.
|
||||
// |!key1=value1&key2=value2|. If there are no non-default attributes, this
|
||||
// returns an empty string.
|
||||
void CreateSuffix(nsACString& aStr);
|
||||
|
||||
void Serialize(nsIObjectOutputStream* aStream) const;
|
||||
nsresult Deserialize(nsIObjectInputStream* aStream);
|
||||
};
|
||||
|
||||
/*
|
||||
* Base class from which all nsIPrincipal implementations inherit. Use this for
|
||||
* default implementations and other commonalities between principal
|
||||
@@ -51,33 +80,7 @@ public:
|
||||
virtual bool IsOnCSSUnprefixingWhitelist() override { return false; }
|
||||
|
||||
static BasePrincipal* Cast(nsIPrincipal* aPrin) { return static_cast<BasePrincipal*>(aPrin); }
|
||||
|
||||
struct OriginAttributes : public dom::OriginAttributesDictionary {
|
||||
OriginAttributes() {}
|
||||
OriginAttributes(uint32_t aAppId, bool aInBrowser)
|
||||
{
|
||||
mAppId = aAppId;
|
||||
mInBrowser = aInBrowser;
|
||||
}
|
||||
|
||||
bool operator==(const OriginAttributes& aOther) const
|
||||
{
|
||||
return mAppId == aOther.mAppId &&
|
||||
mInBrowser == aOther.mInBrowser;
|
||||
}
|
||||
bool operator!=(const OriginAttributes& aOther) const
|
||||
{
|
||||
return !(*this == aOther);
|
||||
}
|
||||
|
||||
// Serializes non-default values into the suffix format, i.e.
|
||||
// |!key1=value1&key2=value2|. If there are no non-default attributes, this
|
||||
// returns an empty string.
|
||||
void CreateSuffix(nsACString& aStr);
|
||||
|
||||
void Serialize(nsIObjectOutputStream* aStream) const;
|
||||
nsresult Deserialize(nsIObjectInputStream* aStream);
|
||||
};
|
||||
static already_AddRefed<BasePrincipal> CreateCodebasePrincipal(nsIURI* aURI, OriginAttributes& aAttrs);
|
||||
|
||||
const OriginAttributes& OriginAttributesRef() { return mOriginAttributes; }
|
||||
uint32_t AppId() const { return mOriginAttributes.mAppId; }
|
||||
|
||||
@@ -26,7 +26,7 @@ class DomainPolicyClone;
|
||||
[ptr] native JSObjectPtr(JSObject);
|
||||
[ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone);
|
||||
|
||||
[scriptable, uuid(ba602ca6-dc7a-457e-a57a-ee5b343fd863)]
|
||||
[scriptable, uuid(f4c578b8-5bac-4ba1-9582-f1140e09a3b4)]
|
||||
interface nsIScriptSecurityManager : nsISupports
|
||||
{
|
||||
/**
|
||||
@@ -179,12 +179,28 @@ interface nsIScriptSecurityManager : nsISupports
|
||||
nsIPrincipal getNoAppCodebasePrincipal(in nsIURI uri);
|
||||
|
||||
/**
|
||||
* Legacy name for getNoAppCodebasePrincipal.
|
||||
* Legacy method for getting a principal with no origin attributes.
|
||||
*
|
||||
* @deprecated use getNoAppCodebasePrincipal instead.
|
||||
* @deprecated use createCodebasePrincipal instead.
|
||||
*/
|
||||
[deprecated] nsIPrincipal getCodebasePrincipal(in nsIURI uri);
|
||||
|
||||
/**
|
||||
* Returns a principal whose origin is composed of |uri| and |originAttributes|.
|
||||
* See nsIPrincipal.h for a description of origin attributes, and
|
||||
* SystemDictionaries.webidl for a list of origin attributes and their defaults.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
nsIPrincipal createCodebasePrincipal(in nsIURI uri, in jsval originAttributes);
|
||||
|
||||
/**
|
||||
* Returns a unique nonce principal with |originAttributes|.
|
||||
* See nsIPrincipal.h for a description of origin attributes, and
|
||||
* SystemDictionaries.webidl for a list of origin attributes and their defaults.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
nsIPrincipal createNullPrincipal(in jsval originAttributes);
|
||||
|
||||
/**
|
||||
* Returns OK if aSourceURI and target have the same "origin"
|
||||
* (scheme, host, and port).
|
||||
|
||||
@@ -54,9 +54,9 @@ public:
|
||||
|
||||
// Returns null on failure.
|
||||
static already_AddRefed<nsNullPrincipal>
|
||||
Create(const OriginAttributes& aOriginAttributes = OriginAttributes());
|
||||
Create(const mozilla::OriginAttributes& aOriginAttributes = mozilla::OriginAttributes());
|
||||
|
||||
nsresult Init(const OriginAttributes& aOriginAttributes = OriginAttributes());
|
||||
nsresult Init(const mozilla::OriginAttributes& aOriginAttributes = mozilla::OriginAttributes());
|
||||
|
||||
virtual void GetScriptLocation(nsACString &aStr) override;
|
||||
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ public:
|
||||
nsPrincipal();
|
||||
|
||||
// Init() must be called before the principal is in a usable state.
|
||||
nsresult Init(nsIURI* aCodebase, const OriginAttributes& aOriginAttributes);
|
||||
nsresult Init(nsIURI* aCodebase, const mozilla::OriginAttributes& aOriginAttributes);
|
||||
|
||||
virtual void GetScriptLocation(nsACString& aStr) override;
|
||||
void SetURI(nsIURI* aURI);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "nsINestedURI.h"
|
||||
#include "nspr.h"
|
||||
#include "nsJSPrincipals.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "nsSystemPrincipal.h"
|
||||
#include "nsPrincipal.h"
|
||||
#include "nsNullPrincipal.h"
|
||||
@@ -357,8 +358,10 @@ nsScriptSecurityManager::GetChannelURIPrincipal(nsIChannel* aChannel,
|
||||
return GetLoadContextCodebasePrincipal(uri, loadContext, aPrincipal);
|
||||
}
|
||||
|
||||
return GetCodebasePrincipalInternal(uri, UNKNOWN_APP_ID,
|
||||
/* isInBrowserElement */ false, aPrincipal);
|
||||
OriginAttributes attrs(UNKNOWN_APP_ID, false);
|
||||
nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(uri, attrs);
|
||||
prin.forget(aPrincipal);
|
||||
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -968,55 +971,24 @@ nsScriptSecurityManager::GetSystemPrincipal(nsIPrincipal **result)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, uint32_t aAppId,
|
||||
bool aInMozBrowser,
|
||||
nsIPrincipal **result)
|
||||
{
|
||||
// I _think_ it's safe to not create null principals here based on aURI.
|
||||
// At least all the callers would do the right thing in those cases, as far
|
||||
// as I can tell. --bz
|
||||
|
||||
nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(aURI);
|
||||
if (uriPrinc) {
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
uriPrinc->GetPrincipal(getter_AddRefs(principal));
|
||||
if (!principal) {
|
||||
principal = nsNullPrincipal::Create();
|
||||
NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
principal.forget(result);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
BasePrincipal::OriginAttributes attrs(aAppId, aInMozBrowser);
|
||||
nsRefPtr<nsPrincipal> codebase = new nsPrincipal();
|
||||
nsresult rv = codebase->Init(aURI, attrs);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
NS_ADDREF(*result = codebase);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::GetSimpleCodebasePrincipal(nsIURI* aURI,
|
||||
nsIPrincipal** aPrincipal)
|
||||
{
|
||||
return GetCodebasePrincipalInternal(aURI,
|
||||
nsIScriptSecurityManager::UNKNOWN_APP_ID,
|
||||
false, aPrincipal);
|
||||
OriginAttributes attrs(UNKNOWN_APP_ID, false);
|
||||
nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
|
||||
prin.forget(aPrincipal);
|
||||
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::GetNoAppCodebasePrincipal(nsIURI* aURI,
|
||||
nsIPrincipal** aPrincipal)
|
||||
{
|
||||
return GetCodebasePrincipalInternal(aURI, nsIScriptSecurityManager::NO_APP_ID,
|
||||
false, aPrincipal);
|
||||
OriginAttributes attrs(NO_APP_ID, false);
|
||||
nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
|
||||
prin.forget(aPrincipal);
|
||||
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -1026,6 +998,33 @@ nsScriptSecurityManager::GetCodebasePrincipal(nsIURI* aURI,
|
||||
return GetNoAppCodebasePrincipal(aURI, aPrincipal);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, JS::Handle<JS::Value> aOriginAttributes,
|
||||
JSContext* aCx, nsIPrincipal** aPrincipal)
|
||||
{
|
||||
OriginAttributes attrs;
|
||||
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
|
||||
prin.forget(aPrincipal);
|
||||
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::CreateNullPrincipal(JS::Handle<JS::Value> aOriginAttributes,
|
||||
JSContext* aCx, nsIPrincipal** aPrincipal)
|
||||
{
|
||||
OriginAttributes attrs;
|
||||
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
nsCOMPtr<nsIPrincipal> prin = nsNullPrincipal::Create(attrs);
|
||||
NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
|
||||
prin.forget(aPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::GetAppCodebasePrincipal(nsIURI* aURI,
|
||||
uint32_t aAppId,
|
||||
@@ -1035,7 +1034,10 @@ nsScriptSecurityManager::GetAppCodebasePrincipal(nsIURI* aURI,
|
||||
NS_ENSURE_TRUE(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
|
||||
NS_ERROR_INVALID_ARG);
|
||||
|
||||
return GetCodebasePrincipalInternal(aURI, aAppId, aInMozBrowser, aPrincipal);
|
||||
OriginAttributes attrs(aAppId, aInMozBrowser);
|
||||
nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
|
||||
prin.forget(aPrincipal);
|
||||
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -1044,14 +1046,13 @@ nsScriptSecurityManager::
|
||||
nsILoadContext* aLoadContext,
|
||||
nsIPrincipal** aPrincipal)
|
||||
{
|
||||
uint32_t appId;
|
||||
aLoadContext->GetAppId(&appId);
|
||||
bool isInBrowserElement;
|
||||
aLoadContext->GetIsInBrowserElement(&isInBrowserElement);
|
||||
return GetCodebasePrincipalInternal(aURI,
|
||||
appId,
|
||||
isInBrowserElement,
|
||||
aPrincipal);
|
||||
// XXXbholley - Make this more general in bug 1165466.
|
||||
OriginAttributes attrs;
|
||||
aLoadContext->GetAppId(&attrs.mAppId);
|
||||
aLoadContext->GetIsInBrowserElement(&attrs.mInBrowser);
|
||||
nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
|
||||
prin.forget(aPrincipal);
|
||||
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -1059,37 +1060,11 @@ nsScriptSecurityManager::GetDocShellCodebasePrincipal(nsIURI* aURI,
|
||||
nsIDocShell* aDocShell,
|
||||
nsIPrincipal** aPrincipal)
|
||||
{
|
||||
return GetCodebasePrincipalInternal(aURI,
|
||||
aDocShell->GetAppId(),
|
||||
aDocShell->GetIsInBrowserElement(),
|
||||
aPrincipal);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsScriptSecurityManager::GetCodebasePrincipalInternal(nsIURI *aURI,
|
||||
uint32_t aAppId,
|
||||
bool aInMozBrowser,
|
||||
nsIPrincipal **result)
|
||||
{
|
||||
NS_ENSURE_ARG(aURI);
|
||||
|
||||
bool inheritsPrincipal;
|
||||
nsresult rv =
|
||||
NS_URIChainHasFlags(aURI,
|
||||
nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
|
||||
&inheritsPrincipal);
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
if (NS_FAILED(rv) || inheritsPrincipal) {
|
||||
principal = nsNullPrincipal::Create();
|
||||
NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
|
||||
} else {
|
||||
rv = CreateCodebasePrincipal(aURI, aAppId, aInMozBrowser,
|
||||
getter_AddRefs(principal));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
principal.forget(result);
|
||||
|
||||
return NS_OK;
|
||||
// XXXbholley - Make this more general in bug 1165466.
|
||||
OriginAttributes attrs(aDocShell->GetAppId(), aDocShell->GetIsInBrowserElement());
|
||||
nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
|
||||
prin.forget(aPrincipal);
|
||||
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -22,6 +22,10 @@ class nsIIOService;
|
||||
class nsIStringBundle;
|
||||
class nsSystemPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
class OriginAttributes;
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// nsScriptSecurityManager //
|
||||
/////////////////////////////
|
||||
@@ -99,15 +103,6 @@ private:
|
||||
// should error out at that point.
|
||||
static nsIPrincipal* doGetObjectPrincipal(JSObject* obj);
|
||||
|
||||
nsresult
|
||||
GetCodebasePrincipalInternal(nsIURI* aURI, uint32_t aAppId,
|
||||
bool aInMozBrowser,
|
||||
nsIPrincipal** result);
|
||||
|
||||
nsresult
|
||||
CreateCodebasePrincipal(nsIURI* aURI, uint32_t aAppId, bool aInMozBrowser,
|
||||
nsIPrincipal** result);
|
||||
|
||||
nsresult
|
||||
Init();
|
||||
|
||||
|
||||
@@ -193,7 +193,6 @@
|
||||
#include "nsIWidget.h"
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/URLSearchParams.h"
|
||||
#include "nsPerformance.h"
|
||||
|
||||
#ifdef MOZ_TOOLKIT_SEARCH
|
||||
@@ -2039,24 +2038,6 @@ nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest,
|
||||
mLSHE->GetIsSubFrame(&isSubFrame);
|
||||
}
|
||||
|
||||
// nsDocShell owns a URLSearchParams that is used by
|
||||
// window.location.searchParams to be in sync with the current location.
|
||||
if (!mURLSearchParams) {
|
||||
mURLSearchParams = new URLSearchParams();
|
||||
}
|
||||
|
||||
nsAutoCString search;
|
||||
|
||||
nsCOMPtr<nsIURL> url(do_QueryInterface(mCurrentURI));
|
||||
if (url) {
|
||||
nsresult rv = url->GetQuery(search);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to get the query from a nsIURL.");
|
||||
}
|
||||
}
|
||||
|
||||
mURLSearchParams->ParseInput(search, nullptr);
|
||||
|
||||
if (!isSubFrame && !isRoot) {
|
||||
/*
|
||||
* We don't want to send OnLocationChange notifications when
|
||||
@@ -2827,16 +2808,16 @@ nsDocShell::GetBusyFlags(uint32_t* aBusyFlags)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::TabToTreeOwner(bool aForward, bool* aTookFocus)
|
||||
nsDocShell::TabToTreeOwner(bool aForward, bool aForDocumentNavigation, bool* aTookFocus)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aTookFocus);
|
||||
|
||||
nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
|
||||
if (chromeFocus) {
|
||||
if (aForward) {
|
||||
*aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement());
|
||||
*aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement(aForDocumentNavigation));
|
||||
} else {
|
||||
*aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement());
|
||||
*aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement(aForDocumentNavigation));
|
||||
}
|
||||
} else {
|
||||
*aTookFocus = false;
|
||||
@@ -5869,11 +5850,6 @@ nsDocShell::Destroy()
|
||||
mParentWidget = nullptr;
|
||||
mCurrentURI = nullptr;
|
||||
|
||||
if (mURLSearchParams) {
|
||||
mURLSearchParams->RemoveObservers();
|
||||
mURLSearchParams = nullptr;
|
||||
}
|
||||
|
||||
if (mScriptGlobal) {
|
||||
mScriptGlobal->DetachFromDocShell();
|
||||
mScriptGlobal = nullptr;
|
||||
@@ -14115,12 +14091,6 @@ nsDocShell::GetOpener()
|
||||
return opener;
|
||||
}
|
||||
|
||||
URLSearchParams*
|
||||
nsDocShell::GetURLSearchParams()
|
||||
{
|
||||
return mURLSearchParams;
|
||||
}
|
||||
|
||||
class JavascriptTimelineMarker : public TimelineMarker
|
||||
{
|
||||
public:
|
||||
@@ -14128,7 +14098,9 @@ public:
|
||||
const char* aReason,
|
||||
const char16_t* aFunctionName,
|
||||
const char16_t* aFileName,
|
||||
uint32_t aLineNumber)
|
||||
uint32_t aLineNumber,
|
||||
JS::Handle<JS::Value> aAsyncStack,
|
||||
JS::Handle<JS::Value> aAsyncCause)
|
||||
: TimelineMarker(aDocShell, aName, TRACING_INTERVAL_START,
|
||||
NS_ConvertUTF8toUTF16(aReason),
|
||||
NO_STACK)
|
||||
@@ -14136,6 +14108,11 @@ public:
|
||||
, mFileName(aFileName)
|
||||
, mLineNumber(aLineNumber)
|
||||
{
|
||||
JSContext* ctx = nsContentUtils::GetCurrentJSContext();
|
||||
if (ctx) {
|
||||
mAsyncStack.init(ctx, aAsyncStack);
|
||||
mAsyncCause.init(ctx, aAsyncCause);
|
||||
}
|
||||
}
|
||||
|
||||
void AddDetails(JSContext* aCx, mozilla::dom::ProfileTimelineMarker& aMarker)
|
||||
@@ -14148,6 +14125,17 @@ public:
|
||||
stackFrame.mLine.Construct(mLineNumber);
|
||||
stackFrame.mSource.Construct(mFileName);
|
||||
stackFrame.mFunctionDisplayName.Construct(mFunctionName);
|
||||
if (mAsyncStack.isObject() && !mAsyncStack.isNullOrUndefined() &&
|
||||
mAsyncCause.isString()) {
|
||||
JS::Rooted<JSObject*> asyncStack(aCx, mAsyncStack.toObjectOrNull());
|
||||
JS::Rooted<JSString*> asyncCause(aCx, mAsyncCause.toString());
|
||||
JS::Rooted<JSObject*> parentFrame(aCx);
|
||||
if (!JS::CopyAsyncStack(aCx, asyncStack, asyncCause, &parentFrame, 0)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
} else {
|
||||
stackFrame.mAsyncParent = parentFrame;
|
||||
}
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> newStack(aCx);
|
||||
if (ToJSValue(aCx, stackFrame, &newStack)) {
|
||||
@@ -14164,13 +14152,17 @@ private:
|
||||
nsString mFunctionName;
|
||||
nsString mFileName;
|
||||
uint32_t mLineNumber;
|
||||
JS::PersistentRooted<JS::Value> mAsyncStack;
|
||||
JS::PersistentRooted<JS::Value> mAsyncCause;
|
||||
};
|
||||
|
||||
void
|
||||
nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
|
||||
const char16_t* aFunctionName,
|
||||
const char16_t* aFilename,
|
||||
const uint32_t aLineNumber)
|
||||
const uint32_t aLineNumber,
|
||||
JS::Handle<JS::Value> aAsyncStack,
|
||||
JS::Handle<JS::Value> aAsyncCause)
|
||||
{
|
||||
bool timelineOn = nsIDocShell::GetRecordProfileTimelineMarkers();
|
||||
|
||||
@@ -14179,7 +14171,8 @@ nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
|
||||
mozilla::UniquePtr<TimelineMarker> marker =
|
||||
MakeUnique<JavascriptTimelineMarker>(this, "Javascript", aReason,
|
||||
aFunctionName, aFilename,
|
||||
aLineNumber);
|
||||
aLineNumber, aAsyncStack,
|
||||
aAsyncCause);
|
||||
AddProfileTimelineMarker(Move(marker));
|
||||
}
|
||||
mJSRunToCompletionDepth++;
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class EventTarget;
|
||||
class URLSearchParams;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,9 +812,6 @@ protected:
|
||||
nsCOMPtr<nsIChannel> mFailedChannel;
|
||||
uint32_t mFailedLoadType;
|
||||
|
||||
// window.location.searchParams is updated in sync with this object.
|
||||
nsRefPtr<mozilla::dom::URLSearchParams> mURLSearchParams;
|
||||
|
||||
// Set in DoURILoad when either the LOAD_RELOAD_ALLOW_MIXED_CONTENT flag or
|
||||
// the LOAD_NORMAL_ALLOW_MIXED_CONTENT flag is set.
|
||||
// Checked in nsMixedContentBlocker, to see if the channels match.
|
||||
|
||||
@@ -11,13 +11,6 @@
|
||||
#include "js/TypeDecls.h"
|
||||
class nsPresContext;
|
||||
class nsIPresShell;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class URLSearchParams;
|
||||
}
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
/**
|
||||
@@ -26,7 +19,6 @@ class URLSearchParams;
|
||||
|
||||
[ptr] native nsPresContext(nsPresContext);
|
||||
[ptr] native nsIPresShell(nsIPresShell);
|
||||
[ptr] native URLSearchParams(mozilla::dom::URLSearchParams);
|
||||
|
||||
interface nsIURI;
|
||||
interface nsIChannel;
|
||||
@@ -54,7 +46,7 @@ interface nsITabParent;
|
||||
|
||||
typedef unsigned long nsLoadFlags;
|
||||
|
||||
[scriptable, builtinclass, uuid(f84b1ae4-2f78-4bad-b36a-6a8516ee6e40)]
|
||||
[scriptable, builtinclass, uuid(9f2babc4-4c2a-4cf7-929f-a1efc325b0df)]
|
||||
interface nsIDocShell : nsIDocShellTreeItem
|
||||
{
|
||||
/**
|
||||
@@ -349,9 +341,12 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||
/*
|
||||
* Tells the docshell to offer focus to its tree owner.
|
||||
* This is currently only necessary for embedding chrome.
|
||||
* If forDocumentNavigation is true, then document navigation should be
|
||||
* performed, where only the root of documents are selected. Otherwise, the
|
||||
* next element in the parent should be returned. Returns true if focus was
|
||||
* successfully taken by the tree owner.
|
||||
*/
|
||||
void tabToTreeOwner(in boolean forward,
|
||||
out boolean tookFocus);
|
||||
bool tabToTreeOwner(in boolean forward, in boolean forDocumentNavigation);
|
||||
|
||||
/**
|
||||
* Current busy state for DocShell
|
||||
@@ -1025,9 +1020,6 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||
[noscript,notxpcom,nostdcall] void setOpener(in nsITabParent aOpener);
|
||||
[noscript,notxpcom,nostdcall] nsITabParent getOpener();
|
||||
|
||||
// URLSearchParams for the window.location is owned by the docShell.
|
||||
[noscript,notxpcom] URLSearchParams getURLSearchParams();
|
||||
|
||||
/**
|
||||
* Notify DocShell when the browser is about to start executing JS, and after
|
||||
* that execution has stopped. This only occurs when the Timeline devtool
|
||||
@@ -1036,7 +1028,9 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||
[noscript,notxpcom,nostdcall] void notifyJSRunToCompletionStart(in string aReason,
|
||||
in wstring functionName,
|
||||
in wstring fileName,
|
||||
in unsigned long lineNumber);
|
||||
in unsigned long lineNumber,
|
||||
in jsval asyncStack,
|
||||
in jsval asyncCause);
|
||||
[noscript,notxpcom,nostdcall] void notifyJSRunToCompletionStop();
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,4 +31,50 @@ let TESTS = [{
|
||||
}
|
||||
}];
|
||||
|
||||
if (Services.prefs.getBoolPref("javascript.options.asyncstack")) {
|
||||
TESTS.push({
|
||||
desc: "Async stack trace on Javascript marker",
|
||||
searchFor: (markers) => {
|
||||
return markers.some(m => (m.name == "Javascript" &&
|
||||
m.causeName == "promise callback"));
|
||||
},
|
||||
setup: function(docShell) {
|
||||
content.dispatchEvent(new content.Event("promisetest"));
|
||||
},
|
||||
check: function(markers) {
|
||||
markers = markers.filter(m => (m.name == "Javascript" &&
|
||||
m.causeName == "promise callback"));
|
||||
ok(markers.length > 0, "Found a Javascript marker");
|
||||
|
||||
let frame = markers[0].stack;
|
||||
ok(frame.asyncParent !== null, "Parent frame has async parent");
|
||||
is(frame.asyncParent.asyncCause, "Promise",
|
||||
"Async parent has correct cause");
|
||||
is(frame.asyncParent.functionDisplayName, "do_promise",
|
||||
"Async parent has correct function name");
|
||||
}
|
||||
}, {
|
||||
desc: "Async stack trace on Javascript marker with script",
|
||||
searchFor: (markers) => {
|
||||
return markers.some(m => (m.name == "Javascript" &&
|
||||
m.causeName == "promise callback"));
|
||||
},
|
||||
setup: function(docShell) {
|
||||
content.dispatchEvent(new content.Event("promisescript"));
|
||||
},
|
||||
check: function(markers) {
|
||||
markers = markers.filter(m => (m.name == "Javascript" &&
|
||||
m.causeName == "promise callback"));
|
||||
ok(markers.length > 0, "Found a Javascript marker");
|
||||
|
||||
let frame = markers[0].stack;
|
||||
ok(frame.asyncParent !== null, "Parent frame has async parent");
|
||||
is(frame.asyncParent.asyncCause, "Promise",
|
||||
"Async parent has correct cause");
|
||||
is(frame.asyncParent.functionDisplayName, "do_promise_script",
|
||||
"Async parent has correct function name");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
timelineContentTest(TESTS);
|
||||
|
||||
@@ -21,6 +21,33 @@
|
||||
}
|
||||
|
||||
window.addEventListener("dog", do_xhr, true);
|
||||
|
||||
function do_promise() {
|
||||
new Promise(function(resolve, reject) {
|
||||
console.time("Bob");
|
||||
window.setTimeout(function() {
|
||||
resolve(23);
|
||||
}, 10);
|
||||
}).then(function (val) {
|
||||
console.timeEnd("Bob");
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("promisetest", do_promise, true);
|
||||
|
||||
var globalResolver;
|
||||
function do_promise_script() {
|
||||
new Promise(function(resolve, reject) {
|
||||
console.time("Bob");
|
||||
globalResolver = resolve;
|
||||
window.setTimeout("globalResolver(23);", 10);
|
||||
}).then(function (val) {
|
||||
console.timeEnd("Bob");
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("promisescript", do_promise_script, true);
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -600,7 +600,8 @@ AutoEntryScript::DocshellEntryMonitor::DocshellEntryMonitor(JSContext* aCx,
|
||||
|
||||
void
|
||||
AutoEntryScript::DocshellEntryMonitor::Entry(JSContext* aCx, JSFunction* aFunction,
|
||||
JSScript* aScript)
|
||||
JSScript* aScript, JS::Handle<JS::Value> aAsyncStack,
|
||||
JS::Handle<JSString*> aAsyncCause)
|
||||
{
|
||||
JS::Rooted<JSFunction*> rootedFunction(aCx);
|
||||
if (aFunction) {
|
||||
@@ -645,10 +646,13 @@ AutoEntryScript::DocshellEntryMonitor::Entry(JSContext* aCx, JSFunction* aFuncti
|
||||
const char16_t* functionNameChars = functionName.isTwoByte() ?
|
||||
functionName.twoByteChars() : nullptr;
|
||||
|
||||
JS::Rooted<JS::Value> asyncCauseValue(aCx, aAsyncCause ? StringValue(aAsyncCause) :
|
||||
JS::NullValue());
|
||||
docShellForJSRunToCompletion->NotifyJSRunToCompletionStart(mReason,
|
||||
functionNameChars,
|
||||
filename.BeginReading(),
|
||||
lineNumber);
|
||||
lineNumber, aAsyncStack,
|
||||
asyncCauseValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -359,20 +359,26 @@ private:
|
||||
public:
|
||||
DocshellEntryMonitor(JSContext* aCx, const char* aReason);
|
||||
|
||||
void Entry(JSContext* aCx, JSFunction* aFunction) override
|
||||
void Entry(JSContext* aCx, JSFunction* aFunction,
|
||||
JS::Handle<JS::Value> aAsyncStack,
|
||||
JS::Handle<JSString*> aAsyncCause) override
|
||||
{
|
||||
Entry(aCx, aFunction, nullptr);
|
||||
Entry(aCx, aFunction, nullptr, aAsyncStack, aAsyncCause);
|
||||
}
|
||||
|
||||
void Entry(JSContext* aCx, JSScript* aScript) override
|
||||
void Entry(JSContext* aCx, JSScript* aScript,
|
||||
JS::Handle<JS::Value> aAsyncStack,
|
||||
JS::Handle<JSString*> aAsyncCause) override
|
||||
{
|
||||
Entry(aCx, nullptr, aScript);
|
||||
Entry(aCx, nullptr, aScript, aAsyncStack, aAsyncCause);
|
||||
}
|
||||
|
||||
void Exit(JSContext* aCx) override;
|
||||
|
||||
private:
|
||||
void Entry(JSContext* aCx, JSFunction* aFunction, JSScript* aScript);
|
||||
void Entry(JSContext* aCx, JSFunction* aFunction, JSScript* aScript,
|
||||
JS::Handle<JS::Value> aAsyncStack,
|
||||
JS::Handle<JSString*> aAsyncCause);
|
||||
|
||||
const char* mReason;
|
||||
};
|
||||
|
||||
@@ -349,7 +349,7 @@ nsresult
|
||||
nsDOMClassInfo::DefineStaticJSVals(JSContext *cx)
|
||||
{
|
||||
#define SET_JSID_TO_STRING(_id, _cx, _str) \
|
||||
if (JSString *str = ::JS_InternString(_cx, _str)) \
|
||||
if (JSString *str = ::JS_AtomizeAndPinString(_cx, _str)) \
|
||||
_id = INTERNED_STRING_TO_JSID(_cx, str); \
|
||||
else \
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
+302
-402
@@ -16,6 +16,7 @@
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIEditor.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIDOMChromeWindow.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMRange.h"
|
||||
@@ -33,6 +34,7 @@
|
||||
#include "nsFrameSelection.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "nsXULPopupManager.h"
|
||||
#include "nsMenuPopupFrame.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIObserverService.h"
|
||||
@@ -41,6 +43,7 @@
|
||||
#include "nsStyleCoord.h"
|
||||
#include "SelectionCarets.h"
|
||||
#include "TabChild.h"
|
||||
#include "nsFrameLoader.h"
|
||||
|
||||
#include "mozilla/ContentEvents.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
@@ -521,6 +524,10 @@ nsFocusManager::MoveFocus(nsIDOMWindow* aWindow, nsIDOMElement* aStartElement,
|
||||
nsCOMPtr<nsIContent> newFocus;
|
||||
nsresult rv = DetermineElementToMoveFocus(window, startContent, aType, noParentTraversal,
|
||||
getter_AddRefs(newFocus));
|
||||
if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOGCONTENTNAVIGATION("Element to be focused: %s", newFocus.get());
|
||||
@@ -2348,7 +2355,8 @@ nsFocusManager::GetSelectionLocation(nsIDocument* aDocument,
|
||||
eLeaf,
|
||||
false, // aVisual
|
||||
false, // aLockInScrollView
|
||||
true // aFollowOOFs
|
||||
true, // aFollowOOFs
|
||||
false // aSkipPopupChecks
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@@ -2408,9 +2416,19 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
{
|
||||
*aNextContent = nullptr;
|
||||
|
||||
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
||||
if (!docShell)
|
||||
return NS_OK;
|
||||
// True if we are navigating by document (F6/Shift+F6) or false if we are
|
||||
// navigating by element (Tab/Shift+Tab).
|
||||
bool forDocumentNavigation = false;
|
||||
|
||||
// This is used for document navigation only. It will be set to true if we
|
||||
// start navigating from a starting point. If this starting point is near the
|
||||
// end of the document (for example, an element on a statusbar), and there
|
||||
// are no child documents or panels before the end of the document, then we
|
||||
// will need to ensure that we don't consider the root chrome window when we
|
||||
// loop around and instead find the next child document/panel, as focus is
|
||||
// already in that window. This flag will be cleared once we navigate into
|
||||
// another document.
|
||||
bool mayFocusRoot = (aStartContent != nullptr);
|
||||
|
||||
nsCOMPtr<nsIContent> startContent = aStartContent;
|
||||
if (!startContent && aType != MOVEFOCUS_CARET) {
|
||||
@@ -2436,19 +2454,24 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
LookAndFeel::GetInt(LookAndFeel::eIntID_TabFocusModel,
|
||||
&nsIContent::sTabFocusModel);
|
||||
|
||||
if (aType == MOVEFOCUS_ROOT) {
|
||||
// These types are for document navigation using F6.
|
||||
if (aType == MOVEFOCUS_FORWARDDOC || aType == MOVEFOCUS_BACKWARDDOC ||
|
||||
aType == MOVEFOCUS_FIRSTDOC || aType == MOVEFOCUS_LASTDOC) {
|
||||
forDocumentNavigation = true;
|
||||
}
|
||||
|
||||
// If moving to the root or first document, find the root element and return.
|
||||
if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_FIRSTDOC) {
|
||||
NS_IF_ADDREF(*aNextContent = GetRootForFocus(aWindow, doc, false, false));
|
||||
return NS_OK;
|
||||
if (!*aNextContent && aType == MOVEFOCUS_FIRSTDOC) {
|
||||
// When looking for the first document, if the root wasn't focusable,
|
||||
// find the next focusable document.
|
||||
aType = MOVEFOCUS_FORWARDDOC;
|
||||
} else {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
if (aType == MOVEFOCUS_FORWARDDOC) {
|
||||
NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(startContent, true));
|
||||
return NS_OK;
|
||||
}
|
||||
if (aType == MOVEFOCUS_BACKWARDDOC) {
|
||||
NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(startContent, false));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsIContent* rootContent = doc->GetRootElement();
|
||||
NS_ENSURE_TRUE(rootContent, NS_OK);
|
||||
|
||||
@@ -2460,17 +2483,19 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
startContent = rootContent;
|
||||
return GetNextTabbableContent(presShell, startContent,
|
||||
nullptr, startContent,
|
||||
true, 1, false, aNextContent);
|
||||
true, 1, false, false, aNextContent);
|
||||
}
|
||||
if (aType == MOVEFOCUS_LAST) {
|
||||
if (!aStartContent)
|
||||
startContent = rootContent;
|
||||
return GetNextTabbableContent(presShell, startContent,
|
||||
nullptr, startContent,
|
||||
false, 0, false, aNextContent);
|
||||
false, 0, false, false, aNextContent);
|
||||
}
|
||||
|
||||
bool forward = (aType == MOVEFOCUS_FORWARD || aType == MOVEFOCUS_CARET);
|
||||
bool forward = (aType == MOVEFOCUS_FORWARD ||
|
||||
aType == MOVEFOCUS_FORWARDDOC ||
|
||||
aType == MOVEFOCUS_CARET);
|
||||
bool doNavigation = true;
|
||||
bool ignoreTabIndex = false;
|
||||
// when a popup is open, we want to ensure that tab navigation occurs only
|
||||
@@ -2505,7 +2530,7 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
nsGkAtoms::menuPopupFrame);
|
||||
}
|
||||
|
||||
if (popupFrame) {
|
||||
if (popupFrame && !forDocumentNavigation) {
|
||||
// Don't navigate outside of a popup, so pretend that the
|
||||
// root content is the popup itself
|
||||
rootContent = popupFrame->GetContent();
|
||||
@@ -2537,13 +2562,24 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
}
|
||||
#endif
|
||||
if (popupFrame) {
|
||||
rootContent = popupFrame->GetContent();
|
||||
NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
|
||||
startContent = rootContent;
|
||||
// When there is a popup open, and no starting content, start the search
|
||||
// at the topmost popup.
|
||||
startContent = popupFrame->GetContent();
|
||||
NS_ASSERTION(startContent, "Popup frame doesn't have a content node");
|
||||
// Unless we are searching for documents, set the root content to the
|
||||
// popup as well, so that we don't tab-navigate outside the popup.
|
||||
// When navigating by documents, we start at the popup but can navigate
|
||||
// outside of it to look for other panels and documents.
|
||||
if (!forDocumentNavigation) {
|
||||
rootContent = startContent;
|
||||
}
|
||||
|
||||
doc = startContent ? startContent->GetComposedDoc() : nullptr;
|
||||
}
|
||||
else {
|
||||
// Otherwise, for content shells, start from the location of the caret.
|
||||
if (docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
|
||||
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
||||
if (docShell && docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
|
||||
nsCOMPtr<nsIContent> endSelectionContent;
|
||||
GetSelectionLocation(doc, presShell,
|
||||
getter_AddRefs(startContent),
|
||||
@@ -2597,7 +2633,8 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
nsIContent* originalStartContent = startContent;
|
||||
|
||||
LOGCONTENTNAVIGATION("Focus Navigation Start Content %s", startContent.get());
|
||||
LOGFOCUSNAVIGATION((" Tabindex: %d Ignore: %d", tabIndex, ignoreTabIndex));
|
||||
LOGFOCUSNAVIGATION((" Forward: %d Tabindex: %d Ignore: %d DocNav: %d",
|
||||
forward, tabIndex, ignoreTabIndex, forDocumentNavigation));
|
||||
|
||||
while (doc) {
|
||||
if (doNavigation) {
|
||||
@@ -2606,22 +2643,29 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
skipOriginalContentCheck ? nullptr : originalStartContent,
|
||||
startContent, forward,
|
||||
tabIndex, ignoreTabIndex,
|
||||
forDocumentNavigation,
|
||||
getter_AddRefs(nextFocus));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
|
||||
// Navigation was redirected to a child process, so just return.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// found a content node to focus.
|
||||
if (nextFocus) {
|
||||
LOGCONTENTNAVIGATION("Next Content: %s", nextFocus.get());
|
||||
|
||||
// as long as the found node was not the same as the starting node,
|
||||
// set it as the return value.
|
||||
if (nextFocus != originalStartContent) {
|
||||
// set it as the return value. For document navigation, we can return
|
||||
// the same element in case there is only one content node that could
|
||||
// be returned, for example, in a child process document.
|
||||
if (nextFocus != originalStartContent || forDocumentNavigation) {
|
||||
nextFocus.forget(aNextContent);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (popupFrame) {
|
||||
if (popupFrame && !forDocumentNavigation) {
|
||||
// in a popup, so start again from the beginning of the popup. However,
|
||||
// if we already started at the beginning, then there isn't anything to
|
||||
// focus, so just return
|
||||
@@ -2635,7 +2679,7 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
}
|
||||
|
||||
doNavigation = true;
|
||||
skipOriginalContentCheck = false;
|
||||
skipOriginalContentCheck = forDocumentNavigation;
|
||||
ignoreTabIndex = false;
|
||||
|
||||
if (aNoParentTraversal) {
|
||||
@@ -2647,46 +2691,45 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
continue;
|
||||
}
|
||||
|
||||
// reached the beginning or end of the document. Traverse up to the parent
|
||||
// document and try again.
|
||||
nsCOMPtr<nsIDocShellTreeItem> docShellParent;
|
||||
docShell->GetParent(getter_AddRefs(docShellParent));
|
||||
if (docShellParent) {
|
||||
// move up to the parent shell and try again from there.
|
||||
// Reached the beginning or end of the document. Next, navigate up to the
|
||||
// parent document and try again.
|
||||
nsCOMPtr<nsPIDOMWindow> piWindow = doc->GetWindow();
|
||||
NS_ENSURE_TRUE(piWindow, NS_ERROR_FAILURE);
|
||||
|
||||
// first, get the frame element this window is inside.
|
||||
nsCOMPtr<nsPIDOMWindow> piWindow = docShell->GetWindow();
|
||||
NS_ENSURE_TRUE(piWindow, NS_ERROR_FAILURE);
|
||||
nsCOMPtr<nsIDocShell> docShell = piWindow->GetDocShell();
|
||||
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
||||
|
||||
// Next, retrieve the parent docshell, document and presshell.
|
||||
docShell = do_QueryInterface(docShellParent);
|
||||
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> piParentWindow = docShellParent->GetWindow();
|
||||
NS_ENSURE_TRUE(piParentWindow, NS_ERROR_FAILURE);
|
||||
doc = piParentWindow->GetExtantDoc();
|
||||
// Get the frame element this window is inside and, from that, get the
|
||||
// parent document and presshell. If there is no enclosing frame element,
|
||||
// then this is a top-level, embedded or remote window.
|
||||
startContent = piWindow->GetFrameElementInternal();
|
||||
if (startContent) {
|
||||
doc = startContent->GetComposedDoc();
|
||||
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
||||
|
||||
rootContent = doc->GetRootElement();
|
||||
presShell = doc->GetShell();
|
||||
|
||||
rootContent = doc->GetRootElement();
|
||||
startContent = piWindow->GetFrameElementInternal();
|
||||
if (startContent) {
|
||||
nsIFrame* frame = startContent->GetPrimaryFrame();
|
||||
if (!frame)
|
||||
return NS_OK;
|
||||
// We can focus the root element now that we have moved to another document.
|
||||
mayFocusRoot = true;
|
||||
|
||||
frame->IsFocusable(&tabIndex, 0);
|
||||
if (tabIndex < 0) {
|
||||
tabIndex = 1;
|
||||
ignoreTabIndex = true;
|
||||
}
|
||||
nsIFrame* frame = startContent->GetPrimaryFrame();
|
||||
if (!frame) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// if the frame is inside a popup, make sure to scan only within the
|
||||
// popup. This handles the situation of tabbing amongst elements
|
||||
// inside an iframe which is itself inside a popup. Otherwise,
|
||||
// navigation would move outside the popup when tabbing outside the
|
||||
// iframe.
|
||||
frame->IsFocusable(&tabIndex, 0);
|
||||
if (tabIndex < 0) {
|
||||
tabIndex = 1;
|
||||
ignoreTabIndex = true;
|
||||
}
|
||||
|
||||
// if the frame is inside a popup, make sure to scan only within the
|
||||
// popup. This handles the situation of tabbing amongst elements
|
||||
// inside an iframe which is itself inside a popup. Otherwise,
|
||||
// navigation would move outside the popup when tabbing outside the
|
||||
// iframe.
|
||||
if (!forDocumentNavigation) {
|
||||
popupFrame = nsLayoutUtils::GetClosestFrameOfType(frame,
|
||||
nsGkAtoms::menuPopupFrame);
|
||||
if (popupFrame) {
|
||||
@@ -2694,17 +2737,13 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
|
||||
}
|
||||
}
|
||||
else {
|
||||
startContent = rootContent;
|
||||
tabIndex = forward ? 1 : 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// no parent, so call the tree owner. This will tell the embedder that
|
||||
// it should take the focus.
|
||||
// There is no parent, so call the tree owner. This will tell the
|
||||
// embedder or parent process that it should take the focus.
|
||||
bool tookFocus;
|
||||
docShell->TabToTreeOwner(forward, &tookFocus);
|
||||
// if the tree owner, took the focus, blur the current content
|
||||
docShell->TabToTreeOwner(forward, forDocumentNavigation, &tookFocus);
|
||||
// If the tree owner took the focus, blur the current content.
|
||||
if (tookFocus) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = docShell->GetWindow();
|
||||
if (window->GetFocusedNode() == mFocusedContent)
|
||||
@@ -2714,6 +2753,23 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we have reached the end of the top-level document, focus the
|
||||
// first element in the top-level document. This should always happen
|
||||
// when navigating by document forwards but when navigating backwards,
|
||||
// only do this if we started in another document or within a popup frame.
|
||||
// If the focus started in this window outside a popup however, we should
|
||||
// continue by looping around to the end again.
|
||||
if (forDocumentNavigation && (forward || mayFocusRoot || popupFrame)) {
|
||||
// HTML content documents can have their root element focused (a focus
|
||||
// ring appears around the entire content area frame). This root
|
||||
// appears in the tab order before all of the elements in the document.
|
||||
// Chrome documents however cannot be focused directly, so instead we
|
||||
// focus the first focusable element within the window.
|
||||
// For example, the urlbar.
|
||||
nsIContent* root = GetRootForFocus(piWindow, doc, true, true);
|
||||
return FocusFirst(root, aNextContent);
|
||||
}
|
||||
|
||||
// reset the tab index and start again from the beginning or end
|
||||
startContent = rootContent;
|
||||
tabIndex = forward ? 1 : 0;
|
||||
@@ -2736,6 +2792,7 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
bool aForward,
|
||||
int32_t aCurrentTabIndex,
|
||||
bool aIgnoreTabIndex,
|
||||
bool aForDocumentNavigation,
|
||||
nsIContent** aResultContent)
|
||||
{
|
||||
*aResultContent = nullptr;
|
||||
@@ -2772,13 +2829,17 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
continue;
|
||||
}
|
||||
|
||||
// For tab navigation, pass false for aSkipPopupChecks so that we don't
|
||||
// iterate into or out of a popup. For document naviation pass true to
|
||||
// ignore these boundaries.
|
||||
nsCOMPtr<nsIFrameEnumerator> frameTraversal;
|
||||
nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
|
||||
presContext, startFrame,
|
||||
ePreOrder,
|
||||
false, // aVisual
|
||||
false, // aLockInScrollView
|
||||
true // aFollowOOFs
|
||||
true, // aFollowOOFs
|
||||
aForDocumentNavigation // aSkipPopupChecks
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@@ -2803,6 +2864,51 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
// Walk frames to find something tabbable matching mCurrentTabIndex
|
||||
nsIFrame* frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
||||
while (frame) {
|
||||
nsIContent* currentContent = frame->GetContent();
|
||||
|
||||
// For document navigation, check if this element is an open panel. Since
|
||||
// panels aren't focusable (tabIndex would be -1), we'll just assume that
|
||||
// for document navigation, the tabIndex is 0.
|
||||
if (aForDocumentNavigation && currentContent && (aCurrentTabIndex == 0) &&
|
||||
currentContent->IsXULElement(nsGkAtoms::panel)) {
|
||||
nsMenuPopupFrame* popupFrame = do_QueryFrame(frame);
|
||||
// Check if the panel is open. Closed panels are ignored since you can't
|
||||
// focus anything in them.
|
||||
if (popupFrame && popupFrame->IsOpen()) {
|
||||
// When moving backward, skip the popup we started in otherwise it
|
||||
// will be selected again.
|
||||
bool validPopup = true;
|
||||
if (!aForward) {
|
||||
nsIContent* content = aStartContent;
|
||||
while (content) {
|
||||
if (content == currentContent) {
|
||||
validPopup = false;
|
||||
break;
|
||||
}
|
||||
|
||||
content = content->GetParent();
|
||||
}
|
||||
}
|
||||
|
||||
if (validPopup) {
|
||||
// Since a panel isn't focusable itself, find the first focusable
|
||||
// content within the popup. If there isn't any focusable content
|
||||
// in the popup, skip this popup and continue iterating through the
|
||||
// frames. We pass the panel itself (currentContent) as the starting
|
||||
// and root content, so that we only find content within the panel.
|
||||
// Note also that we pass false for aForDocumentNavigation since we
|
||||
// want to locate the first content, not the first document.
|
||||
rv = GetNextTabbableContent(aPresShell, currentContent,
|
||||
nullptr, currentContent,
|
||||
true, 1, false, false,
|
||||
aResultContent);
|
||||
if (NS_SUCCEEDED(rv) && *aResultContent) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TabIndex not set defaults to 0 for form elements, anchors and other
|
||||
// elements that are normally focusable. Tabindex defaults to -1
|
||||
// for elements that are not normally focusable.
|
||||
@@ -2810,17 +2916,16 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
// < 0 not tabbable at all
|
||||
// == 0 in normal tab order (last after positive tabindexed items)
|
||||
// > 0 can be tabbed to in the order specified by this value
|
||||
|
||||
int32_t tabIndex;
|
||||
frame->IsFocusable(&tabIndex, 0);
|
||||
|
||||
LOGCONTENTNAVIGATION("Next Tabbable %s:", frame->GetContent());
|
||||
LOGFOCUSNAVIGATION((" with tabindex: %d expected: %d", tabIndex, aCurrentTabIndex));
|
||||
|
||||
nsIContent* currentContent = frame->GetContent();
|
||||
if (tabIndex >= 0) {
|
||||
NS_ASSERTION(currentContent, "IsFocusable set a tabindex for a frame with no content");
|
||||
if (currentContent->IsHTMLElement(nsGkAtoms::img) &&
|
||||
if (!aForDocumentNavigation &&
|
||||
currentContent->IsHTMLElement(nsGkAtoms::img) &&
|
||||
currentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usemap)) {
|
||||
// This is an image with a map. Image map areas are not traversed by
|
||||
// nsIFrameTraversal so look for the next or previous area element.
|
||||
@@ -2839,29 +2944,50 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// found a node with a matching tab index. Check if it is a child
|
||||
// frame. If so, navigate into the child frame instead.
|
||||
nsIDocument* doc = currentContent->GetComposedDoc();
|
||||
NS_ASSERTION(doc, "content not in document");
|
||||
nsIDocument* subdoc = doc->GetSubDocumentFor(currentContent);
|
||||
if (subdoc) {
|
||||
if (!subdoc->EventHandlingSuppressed()) {
|
||||
bool checkSubDocument = true;
|
||||
if (aForDocumentNavigation) {
|
||||
// If this is a remote child browser, call NavigateDocument to have
|
||||
// the child process continue the navigation. Return a special error
|
||||
// code to have the caller return early. If the child ends up not
|
||||
// being focusable in some way, the child process will call back
|
||||
// into document navigation again by calling MoveFocus.
|
||||
TabParent* remote = TabParent::GetFrom(currentContent);
|
||||
if (remote) {
|
||||
remote->NavigateDocument(aForward);
|
||||
return NS_SUCCESS_DOM_NO_OPERATION;
|
||||
}
|
||||
|
||||
// Next, check if this a non-remote child document.
|
||||
nsIContent* docRoot = GetRootForChildDocument(currentContent);
|
||||
if (docRoot) {
|
||||
// If GetRootForChildDocument returned something then call
|
||||
// FocusFirst to find the root or first element to focus within
|
||||
// the child document. If this is a frameset though, skip this and
|
||||
// fall through to the checkSubDocument block below to iterate into
|
||||
// the frameset's frames and locate the first focusable frame.
|
||||
if (!docRoot->IsHTMLElement(nsGkAtoms::frameset)) {
|
||||
return FocusFirst(docRoot, aResultContent);
|
||||
}
|
||||
} else {
|
||||
// Set checkSubDocument to false, as this was neither a frame
|
||||
// type element or a child document that was focusable.
|
||||
checkSubDocument = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkSubDocument) {
|
||||
// found a node with a matching tab index. Check if it is a child
|
||||
// frame. If so, navigate into the child frame instead.
|
||||
nsIDocument* doc = currentContent->GetComposedDoc();
|
||||
NS_ASSERTION(doc, "content not in document");
|
||||
nsIDocument* subdoc = doc->GetSubDocumentFor(currentContent);
|
||||
if (subdoc && !subdoc->EventHandlingSuppressed()) {
|
||||
if (aForward) {
|
||||
// when tabbing forward into a frame, return the root
|
||||
// frame so that the canvas becomes focused.
|
||||
nsCOMPtr<nsPIDOMWindow> subframe = subdoc->GetWindow();
|
||||
if (subframe) {
|
||||
// If the subframe body is editable by contenteditable,
|
||||
// we should set the editor's root element rather than the
|
||||
// actual root element. Otherwise, we should set the focus
|
||||
// to the root content.
|
||||
*aResultContent =
|
||||
nsLayoutUtils::GetEditableRootContentByContentEditable(subdoc);
|
||||
if (!*aResultContent ||
|
||||
!((*aResultContent)->GetPrimaryFrame())) {
|
||||
*aResultContent =
|
||||
GetRootForFocus(subframe, subdoc, false, true);
|
||||
}
|
||||
*aResultContent = GetRootForFocus(subframe, subdoc, false, true);
|
||||
if (*aResultContent) {
|
||||
NS_ADDREF(*aResultContent);
|
||||
return NS_OK;
|
||||
@@ -2874,29 +3000,29 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
rv = GetNextTabbableContent(subShell, rootElement,
|
||||
aOriginalStartContent, rootElement,
|
||||
aForward, (aForward ? 1 : 0),
|
||||
false, aResultContent);
|
||||
false, aForDocumentNavigation, aResultContent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (*aResultContent)
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
// otherwise, use this as the next content node to tab to, unless
|
||||
// this was the element we started on. This would happen for
|
||||
// instance on an element with child frames, where frame navigation
|
||||
// could return the original element again. In that case, just skip
|
||||
// it. Also, if the next content node is the root content, then
|
||||
// return it. This latter case would happen only if someone made a
|
||||
// popup focusable.
|
||||
// Also, when going backwards, check to ensure that the focus
|
||||
// wouldn't be redirected. Otherwise, for example, when an input in
|
||||
// a textbox is focused, the enclosing textbox would be found and
|
||||
// the same inner input would be returned again.
|
||||
else if (currentContent == aRootContent ||
|
||||
(currentContent != startContent &&
|
||||
(aForward || !GetRedirectedFocus(currentContent)))) {
|
||||
NS_ADDREF(*aResultContent = currentContent);
|
||||
return NS_OK;
|
||||
// otherwise, use this as the next content node to tab to, unless
|
||||
// this was the element we started on. This would happen for
|
||||
// instance on an element with child frames, where frame navigation
|
||||
// could return the original element again. In that case, just skip
|
||||
// it. Also, if the next content node is the root content, then
|
||||
// return it. This latter case would happen only if someone made a
|
||||
// popup focusable.
|
||||
// Also, when going backwards, check to ensure that the focus
|
||||
// wouldn't be redirected. Otherwise, for example, when an input in
|
||||
// a textbox is focused, the enclosing textbox would be found and
|
||||
// the same inner input would be returned again.
|
||||
else if (currentContent == aRootContent ||
|
||||
(currentContent != startContent &&
|
||||
(aForward || !GetRedirectedFocus(currentContent)))) {
|
||||
NS_ADDREF(*aResultContent = currentContent);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2929,13 +3055,14 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
// A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
|
||||
if (aCurrentTabIndex == (aForward ? 0 : 1)) {
|
||||
// if going backwards, the canvas should be focused once the beginning
|
||||
// has been reached.
|
||||
// has been reached, so get the root element.
|
||||
if (!aForward) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetCurrentWindow(aRootContent);
|
||||
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
|
||||
NS_IF_ADDREF(*aResultContent =
|
||||
GetRootForFocus(window, aRootContent->GetComposedDoc(),
|
||||
false, true));
|
||||
|
||||
nsCOMPtr<nsIContent> docRoot =
|
||||
GetRootForFocus(window, aRootContent->GetComposedDoc(), false, true);
|
||||
FocusFirst(docRoot, aResultContent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -3041,26 +3168,41 @@ nsFocusManager::GetNextTabIndex(nsIContent* aParent,
|
||||
return tabIndex;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFocusManager::FocusFirst(nsIContent* aRootContent, nsIContent** aNextContent)
|
||||
{
|
||||
if (!aRootContent) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIDocument* doc = aRootContent->GetComposedDoc();
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIDocShell> docShell = doc->GetDocShell();
|
||||
if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
|
||||
// If the found content is in a chrome shell, navigate forward one
|
||||
// tabbable item so that the first item is focused. Note that we
|
||||
// always go forward and not back here.
|
||||
nsIPresShell* presShell = doc->GetShell();
|
||||
if (presShell) {
|
||||
return GetNextTabbableContent(presShell, aRootContent,
|
||||
nullptr, aRootContent,
|
||||
true, 1, false, false,
|
||||
aNextContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_ADDREF(*aNextContent = aRootContent);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::GetRootForFocus(nsPIDOMWindow* aWindow,
|
||||
nsIDocument* aDocument,
|
||||
bool aIsForDocNavigation,
|
||||
bool aForDocumentNavigation,
|
||||
bool aCheckVisibility)
|
||||
{
|
||||
// the root element's canvas may be focused as long as the document is in a
|
||||
// a non-chrome shell and does not contain a frameset.
|
||||
if (aIsForDocNavigation) {
|
||||
nsCOMPtr<Element> docElement = aWindow->GetFrameElementInternal();
|
||||
// document navigation skips iframes and frames that are specifically non-focusable
|
||||
if (docElement) {
|
||||
if (docElement->NodeInfo()->NameAtom() == nsGkAtoms::iframe)
|
||||
return nullptr;
|
||||
|
||||
nsIFrame* frame = docElement->GetPrimaryFrame();
|
||||
if (!frame || !frame->IsFocusable(nullptr, 0))
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (!aForDocumentNavigation) {
|
||||
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
||||
if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
|
||||
return nullptr;
|
||||
@@ -3070,9 +3212,15 @@ nsFocusManager::GetRootForFocus(nsPIDOMWindow* aWindow,
|
||||
if (aCheckVisibility && !IsWindowVisible(aWindow))
|
||||
return nullptr;
|
||||
|
||||
Element *rootElement = aDocument->GetRootElement();
|
||||
if (!rootElement) {
|
||||
return nullptr;
|
||||
// If the body is contenteditable, use the editor's root element rather than
|
||||
// the actual root element.
|
||||
nsCOMPtr<nsIContent> rootElement =
|
||||
nsLayoutUtils::GetEditableRootContentByContentEditable(aDocument);
|
||||
if (!rootElement || !rootElement->GetPrimaryFrame()) {
|
||||
rootElement = aDocument->GetRootElement();
|
||||
if (!rootElement) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (aCheckVisibility && !rootElement->GetPrimaryFrame()) {
|
||||
@@ -3081,291 +3229,43 @@ nsFocusManager::GetRootForFocus(nsPIDOMWindow* aWindow,
|
||||
|
||||
// Finally, check if this is a frameset
|
||||
nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aDocument);
|
||||
if (htmlDoc && aDocument->GetHtmlChildElement(nsGkAtoms::frameset)) {
|
||||
return nullptr;
|
||||
if (htmlDoc) {
|
||||
nsIContent* htmlChild = aDocument->GetHtmlChildElement(nsGkAtoms::frameset);
|
||||
if (htmlChild) {
|
||||
// In document navigation mode, return the frameset so that navigation
|
||||
// descends into the child frames.
|
||||
return aForDocumentNavigation ? htmlChild : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return rootElement;
|
||||
}
|
||||
|
||||
void
|
||||
nsFocusManager::GetLastDocShell(nsIDocShellTreeItem* aItem,
|
||||
nsIDocShellTreeItem** aResult)
|
||||
{
|
||||
*aResult = nullptr;
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> curItem = aItem;
|
||||
while (curItem) {
|
||||
int32_t childCount = 0;
|
||||
curItem->GetChildCount(&childCount);
|
||||
if (!childCount) {
|
||||
curItem.forget(aResult);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
curItem->GetChildAt(childCount - 1, getter_AddRefs(curItem));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsFocusManager::GetNextDocShell(nsIDocShellTreeItem* aItem,
|
||||
nsIDocShellTreeItem** aResult)
|
||||
{
|
||||
*aResult = nullptr;
|
||||
|
||||
int32_t childCount = 0;
|
||||
aItem->GetChildCount(&childCount);
|
||||
if (childCount) {
|
||||
aItem->GetChildAt(0, aResult);
|
||||
if (*aResult)
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> curItem = aItem;
|
||||
while (curItem) {
|
||||
nsCOMPtr<nsIDocShellTreeItem> parentItem;
|
||||
curItem->GetParent(getter_AddRefs(parentItem));
|
||||
if (!parentItem)
|
||||
return;
|
||||
|
||||
// Note that we avoid using GetChildOffset() here because docshell
|
||||
// child offsets can't be trusted to be correct. bug 162283.
|
||||
nsCOMPtr<nsIDocShellTreeItem> iterItem;
|
||||
childCount = 0;
|
||||
parentItem->GetChildCount(&childCount);
|
||||
for (int32_t index = 0; index < childCount; ++index) {
|
||||
parentItem->GetChildAt(index, getter_AddRefs(iterItem));
|
||||
if (iterItem == curItem) {
|
||||
++index;
|
||||
if (index < childCount) {
|
||||
parentItem->GetChildAt(index, aResult);
|
||||
if (*aResult)
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
curItem = parentItem;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsFocusManager::GetPreviousDocShell(nsIDocShellTreeItem* aItem,
|
||||
nsIDocShellTreeItem** aResult)
|
||||
{
|
||||
*aResult = nullptr;
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> parentItem;
|
||||
aItem->GetParent(getter_AddRefs(parentItem));
|
||||
if (!parentItem)
|
||||
return;
|
||||
|
||||
// Note that we avoid using GetChildOffset() here because docshell
|
||||
// child offsets can't be trusted to be correct. bug 162283.
|
||||
int32_t childCount = 0;
|
||||
parentItem->GetChildCount(&childCount);
|
||||
nsCOMPtr<nsIDocShellTreeItem> prevItem, iterItem;
|
||||
for (int32_t index = 0; index < childCount; ++index) {
|
||||
parentItem->GetChildAt(index, getter_AddRefs(iterItem));
|
||||
if (iterItem == aItem)
|
||||
break;
|
||||
prevItem = iterItem;
|
||||
}
|
||||
|
||||
if (prevItem)
|
||||
GetLastDocShell(prevItem, aResult);
|
||||
else
|
||||
parentItem.forget(aResult);
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::GetNextTabbablePanel(nsIDocument* aDocument, nsIFrame* aCurrentPopup, bool aForward)
|
||||
nsFocusManager::GetRootForChildDocument(nsIContent* aContent)
|
||||
{
|
||||
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
||||
if (!pm)
|
||||
// Check for elements that represent child documents, that is, browsers,
|
||||
// editors or frames from a frameset. We don't include iframes since we
|
||||
// consider them to be an integral part of the same window or page.
|
||||
if (!aContent ||
|
||||
!(aContent->IsXULElement(nsGkAtoms::browser) ||
|
||||
aContent->IsXULElement(nsGkAtoms::editor) ||
|
||||
aContent->IsHTMLElement(nsGkAtoms::frame))) {
|
||||
return nullptr;
|
||||
|
||||
// Iterate through the array backwards if aForward is false.
|
||||
nsTArray<nsIFrame *> popups;
|
||||
pm->GetVisiblePopups(popups);
|
||||
int32_t i = aForward ? 0 : popups.Length() - 1;
|
||||
int32_t end = aForward ? popups.Length() : -1;
|
||||
|
||||
for (; i != end; aForward ? i++ : i--) {
|
||||
nsIFrame* popupFrame = popups[i];
|
||||
if (aCurrentPopup) {
|
||||
// If the current popup is set, then we need to skip over this popup and
|
||||
// wait until the currently focused popup is found. Once found, the
|
||||
// current popup will be cleared so that the next popup is used.
|
||||
if (aCurrentPopup == popupFrame)
|
||||
aCurrentPopup = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip over non-panels
|
||||
if (!popupFrame->GetContent()->IsXULElement(nsGkAtoms::panel) ||
|
||||
(aDocument && popupFrame->GetContent()->GetComposedDoc() != aDocument)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the first focusable content within the popup. If there isn't any
|
||||
// focusable content in the popup, skip to the next popup.
|
||||
nsIPresShell* presShell = popupFrame->PresContext()->GetPresShell();
|
||||
if (presShell) {
|
||||
nsCOMPtr<nsIContent> nextFocus;
|
||||
nsIContent* popup = popupFrame->GetContent();
|
||||
nsresult rv = GetNextTabbableContent(presShell, popup,
|
||||
nullptr, popup,
|
||||
true, 1, false,
|
||||
getter_AddRefs(nextFocus));
|
||||
if (NS_SUCCEEDED(rv) && nextFocus) {
|
||||
return nextFocus.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::GetNextTabbableDocument(nsIContent* aStartContent, bool aForward)
|
||||
{
|
||||
// If currentPopup is set, then the starting content is in a panel.
|
||||
nsIFrame* currentPopup = nullptr;
|
||||
nsCOMPtr<nsIDocument> doc;
|
||||
nsCOMPtr<nsIDocShell> startDocShell;
|
||||
|
||||
if (aStartContent) {
|
||||
doc = aStartContent->GetComposedDoc();
|
||||
if (doc) {
|
||||
startDocShell = doc->GetWindow()->GetDocShell();
|
||||
}
|
||||
|
||||
// Check if the starting content is inside a panel. Document navigation
|
||||
// must start from this panel instead of the document root.
|
||||
nsIContent* content = aStartContent;
|
||||
while (content) {
|
||||
if (content->NodeInfo()->Equals(nsGkAtoms::panel, kNameSpaceID_XUL)) {
|
||||
currentPopup = content->GetPrimaryFrame();
|
||||
break;
|
||||
}
|
||||
content = content->GetParent();
|
||||
}
|
||||
}
|
||||
else if (mFocusedWindow) {
|
||||
startDocShell = mFocusedWindow->GetDocShell();
|
||||
doc = mFocusedWindow->GetExtantDoc();
|
||||
} else if (mActiveWindow) {
|
||||
startDocShell = mActiveWindow->GetDocShell();
|
||||
doc = mActiveWindow->GetExtantDoc();
|
||||
}
|
||||
|
||||
if (!startDocShell)
|
||||
nsIDocument* doc = aContent->GetComposedDoc();
|
||||
if (!doc) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// perform a depth first search (preorder) of the docshell tree
|
||||
// looking for an HTML Frame or a chrome document
|
||||
nsIContent* content = aStartContent;
|
||||
nsCOMPtr<nsIDocShellTreeItem> curItem = startDocShell.get();
|
||||
nsCOMPtr<nsIDocShellTreeItem> nextItem;
|
||||
do {
|
||||
// If moving forward, check for a panel in the starting document. If one
|
||||
// exists with focusable content, return that content instead of the next
|
||||
// document. If currentPopup is set, then, another panel may exist. If no
|
||||
// such panel exists, then continue on to check the next document.
|
||||
// When moving backwards, and the starting content is in a panel, then
|
||||
// check for additional panels in the starting document. If the starting
|
||||
// content is not in a panel, move back to the previous document and check
|
||||
// for panels there.
|
||||
nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
|
||||
if (!subdoc || subdoc->EventHandlingSuppressed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool checkPopups = false;
|
||||
nsCOMPtr<nsPIDOMWindow> nextFrame = nullptr;
|
||||
|
||||
if (doc && (aForward || currentPopup)) {
|
||||
nsIContent* popupContent = GetNextTabbablePanel(doc, currentPopup, aForward);
|
||||
if (popupContent)
|
||||
return popupContent;
|
||||
|
||||
if (!aForward && currentPopup) {
|
||||
// The starting content was in a popup, yet no other popups were
|
||||
// found. Move onto the starting content's document.
|
||||
nextFrame = doc->GetWindow();
|
||||
}
|
||||
}
|
||||
|
||||
// Look for the next or previous document.
|
||||
if (!nextFrame) {
|
||||
if (aForward) {
|
||||
GetNextDocShell(curItem, getter_AddRefs(nextItem));
|
||||
if (!nextItem) {
|
||||
// wrap around to the beginning, which is the top of the tree
|
||||
startDocShell->GetRootTreeItem(getter_AddRefs(nextItem));
|
||||
}
|
||||
}
|
||||
else {
|
||||
GetPreviousDocShell(curItem, getter_AddRefs(nextItem));
|
||||
if (!nextItem) {
|
||||
// wrap around to the end, which is the last item in the tree
|
||||
nsCOMPtr<nsIDocShellTreeItem> rootItem;
|
||||
startDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
|
||||
GetLastDocShell(rootItem, getter_AddRefs(nextItem));
|
||||
}
|
||||
|
||||
// When going back to the previous document, check for any focusable
|
||||
// popups in that previous document first.
|
||||
checkPopups = true;
|
||||
}
|
||||
|
||||
curItem = nextItem;
|
||||
nextFrame = nextItem ? nextItem->GetWindow() : nullptr;
|
||||
}
|
||||
|
||||
if (!nextFrame)
|
||||
return nullptr;
|
||||
|
||||
// Clear currentPopup for the next iteration
|
||||
currentPopup = nullptr;
|
||||
|
||||
// If event handling is suppressed, move on to the next document. Set
|
||||
// content to null so that the popup check will be skipped on the next
|
||||
// loop iteration.
|
||||
doc = nextFrame->GetExtantDoc();
|
||||
if (!doc || doc->EventHandlingSuppressed()) {
|
||||
content = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (checkPopups) {
|
||||
// When iterating backwards, check the panels of the previous document
|
||||
// first. If a panel exists that has focusable content, focus that.
|
||||
// Otherwise, continue on to focus the document.
|
||||
nsIContent* popupContent = GetNextTabbablePanel(doc, nullptr, false);
|
||||
if (popupContent)
|
||||
return popupContent;
|
||||
}
|
||||
|
||||
content = GetRootForFocus(nextFrame, doc, true, true);
|
||||
if (content && !GetRootForFocus(nextFrame, doc, false, false)) {
|
||||
// if the found content is in a chrome shell or a frameset, navigate
|
||||
// forward one tabbable item so that the first item is focused. Note
|
||||
// that we always go forward and not back here.
|
||||
nsCOMPtr<nsIContent> nextFocus;
|
||||
Element* rootElement = doc->GetRootElement();
|
||||
nsIPresShell* presShell = doc->GetShell();
|
||||
if (presShell) {
|
||||
nsresult rv = GetNextTabbableContent(presShell, rootElement,
|
||||
nullptr, rootElement,
|
||||
true, 1, false,
|
||||
getter_AddRefs(nextFocus));
|
||||
return NS_SUCCEEDED(rv) ? nextFocus.get() : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} while (!content);
|
||||
|
||||
return content;
|
||||
nsCOMPtr<nsPIDOMWindow> window = subdoc->GetWindow();
|
||||
return GetRootForFocus(window, subdoc, true, true);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+19
-45
@@ -25,6 +25,12 @@ class nsIDocShellTreeItem;
|
||||
class nsPIDOMWindow;
|
||||
class nsIMessageBroadcaster;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class TabParent;
|
||||
}
|
||||
}
|
||||
|
||||
struct nsDelayedBlurOrFocusEvent;
|
||||
|
||||
/**
|
||||
@@ -387,6 +393,7 @@ protected:
|
||||
bool aForward,
|
||||
int32_t aCurrentTabIndex,
|
||||
bool aIgnoreTabIndex,
|
||||
bool aForDocumentNavigation,
|
||||
nsIContent** aResultContent);
|
||||
|
||||
/**
|
||||
@@ -415,65 +422,32 @@ protected:
|
||||
int32_t aCurrentTabIndex,
|
||||
bool aForward);
|
||||
|
||||
/**
|
||||
* Focus the first focusable content within the document with a root node of
|
||||
* aRootContent. For content documents, this will be aRootContent itself, but
|
||||
* for chrome documents, this will locate the next focusable content.
|
||||
*/
|
||||
nsresult FocusFirst(nsIContent* aRootContent, nsIContent** aNextContent);
|
||||
|
||||
/**
|
||||
* Retrieves and returns the root node from aDocument to be focused. Will
|
||||
* return null if the root node cannot be focused. There are several reasons
|
||||
* for this:
|
||||
*
|
||||
* - if aIsForDocNavigation is true, and aWindow is in an <iframe>.
|
||||
* - if aIsForDocNavigation is false, and aWindow is a chrome shell.
|
||||
* - if aForDocumentNavigation is false and aWindow is a chrome shell.
|
||||
* - if aCheckVisibility is true and the aWindow is not visible.
|
||||
* - if aDocument is a frameset document.
|
||||
*/
|
||||
nsIContent* GetRootForFocus(nsPIDOMWindow* aWindow,
|
||||
nsIDocument* aDocument,
|
||||
bool aIsForDocNavigation,
|
||||
bool aForDocumentNavigation,
|
||||
bool aCheckVisibility);
|
||||
|
||||
/**
|
||||
* Get the last docshell child of aItem and return it in aResult.
|
||||
* Retrieves and returns the root node as with GetRootForFocus but only if
|
||||
* aContent is a frame with a valid child document.
|
||||
*/
|
||||
void GetLastDocShell(nsIDocShellTreeItem* aItem,
|
||||
nsIDocShellTreeItem** aResult);
|
||||
|
||||
/**
|
||||
* Get the next docshell child of aItem and return it in aResult.
|
||||
*/
|
||||
void GetNextDocShell(nsIDocShellTreeItem* aItem,
|
||||
nsIDocShellTreeItem** aResult);
|
||||
|
||||
/**
|
||||
* Get the previous docshell child of aItem and return it in aResult.
|
||||
*/
|
||||
void GetPreviousDocShell(nsIDocShellTreeItem* aItem,
|
||||
nsIDocShellTreeItem** aResult);
|
||||
|
||||
/**
|
||||
* Determine the first panel with focusable content in document tab order
|
||||
* from the given document. aForward indicates the direction to scan. If
|
||||
* aCurrentPopup is set to a panel, the next or previous popup after
|
||||
* aCurrentPopup after it is used. If aCurrentPopup is null, then the first
|
||||
* or last popup is used. If a panel has no focusable content, it is skipped.
|
||||
* Null is returned if no panel is open or no open panel contains a focusable
|
||||
* element.
|
||||
*/
|
||||
nsIContent* GetNextTabbablePanel(nsIDocument* aDocument, nsIFrame* aCurrentPopup, bool aForward);
|
||||
|
||||
/**
|
||||
* Get the tabbable next document from aStartContent or, if null, the
|
||||
* currently focused frame if aForward is true, or the previously tabbable
|
||||
* document if aForward is false. If this document is a chrome or frameset
|
||||
* document, returns the first focusable element within this document,
|
||||
* otherwise, returns the root node of the document.
|
||||
*
|
||||
*
|
||||
* Panels with focusable content are also placed in the cycling order, just
|
||||
* after the document containing that panel.
|
||||
*
|
||||
* This method would be used for document navigation, which is typically
|
||||
* invoked by pressing F6.
|
||||
*/
|
||||
nsIContent* GetNextTabbableDocument(nsIContent* aStartContent, bool aForward);
|
||||
nsIContent* GetRootForChildDocument(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Retreives a focusable element within the current selection of aWindow.
|
||||
|
||||
@@ -61,7 +61,6 @@ nsLocation::nsLocation(nsPIDOMWindow* aWindow, nsIDocShell *aDocShell)
|
||||
|
||||
nsLocation::~nsLocation()
|
||||
{
|
||||
RemoveURLSearchParams();
|
||||
}
|
||||
|
||||
// QueryInterface implementation for nsLocation
|
||||
@@ -74,14 +73,11 @@ NS_INTERFACE_MAP_END
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsLocation)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsLocation)
|
||||
tmp->RemoveURLSearchParams();
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mInnerWindow);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsLocation)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSearchParams)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInnerWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
@@ -1088,119 +1084,3 @@ nsLocation::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return LocationBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
URLSearchParams*
|
||||
nsLocation::GetDocShellSearchParams()
|
||||
{
|
||||
nsCOMPtr<nsIDocShell> docShell = GetDocShell();
|
||||
if (!docShell) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return docShell->GetURLSearchParams();
|
||||
}
|
||||
|
||||
URLSearchParams*
|
||||
nsLocation::SearchParams()
|
||||
{
|
||||
if (!mSearchParams) {
|
||||
// We must register this object to the URLSearchParams of the docshell in
|
||||
// order to receive updates.
|
||||
nsRefPtr<URLSearchParams> searchParams = GetDocShellSearchParams();
|
||||
if (searchParams) {
|
||||
searchParams->AddObserver(this);
|
||||
}
|
||||
|
||||
mSearchParams = new URLSearchParams();
|
||||
mSearchParams->AddObserver(this);
|
||||
UpdateURLSearchParams();
|
||||
}
|
||||
|
||||
return mSearchParams;
|
||||
}
|
||||
|
||||
void
|
||||
nsLocation::SetSearchParams(URLSearchParams& aSearchParams)
|
||||
{
|
||||
if (mSearchParams) {
|
||||
mSearchParams->RemoveObserver(this);
|
||||
}
|
||||
|
||||
// the observer will be cleared using the cycle collector.
|
||||
mSearchParams = &aSearchParams;
|
||||
mSearchParams->AddObserver(this);
|
||||
|
||||
nsAutoString search;
|
||||
mSearchParams->Serialize(search);
|
||||
SetSearchInternal(search);
|
||||
|
||||
// We don't need to inform the docShell about this new SearchParams because
|
||||
// setting the new value the docShell will refresh its value automatically.
|
||||
}
|
||||
|
||||
void
|
||||
nsLocation::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
|
||||
{
|
||||
MOZ_ASSERT(mSearchParams);
|
||||
|
||||
// This change comes from content.
|
||||
if (aSearchParams == mSearchParams) {
|
||||
nsAutoString search;
|
||||
mSearchParams->Serialize(search);
|
||||
SetSearchInternal(search);
|
||||
return;
|
||||
}
|
||||
|
||||
// This change comes from the docShell.
|
||||
#ifdef DEBUG
|
||||
{
|
||||
nsRefPtr<URLSearchParams> searchParams = GetDocShellSearchParams();
|
||||
MOZ_ASSERT(searchParams);
|
||||
MOZ_ASSERT(aSearchParams == searchParams);
|
||||
}
|
||||
#endif
|
||||
|
||||
nsAutoString search;
|
||||
aSearchParams->Serialize(search);
|
||||
mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(search), this);
|
||||
}
|
||||
|
||||
void
|
||||
nsLocation::UpdateURLSearchParams()
|
||||
{
|
||||
if (!mSearchParams) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoCString search;
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = GetURI(getter_AddRefs(uri));
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!uri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
|
||||
if (url) {
|
||||
nsresult rv = url->GetQuery(search);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to get the query from a nsIURL.");
|
||||
}
|
||||
}
|
||||
|
||||
mSearchParams->ParseInput(search, this);
|
||||
}
|
||||
|
||||
void
|
||||
nsLocation::RemoveURLSearchParams()
|
||||
{
|
||||
if (mSearchParams) {
|
||||
mSearchParams->RemoveObserver(this);
|
||||
mSearchParams = nullptr;
|
||||
|
||||
nsRefPtr<URLSearchParams> docShellSearchParams = GetDocShellSearchParams();
|
||||
if (docShellSearchParams) {
|
||||
docShellSearchParams->RemoveObserver(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/URLSearchParams.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
class nsIURI;
|
||||
@@ -27,7 +26,6 @@ class nsIDocShellLoadInfo;
|
||||
|
||||
class nsLocation final : public nsIDOMLocation
|
||||
, public nsWrapperCache
|
||||
, public mozilla::dom::URLSearchParamsObserver
|
||||
{
|
||||
typedef mozilla::ErrorResult ErrorResult;
|
||||
|
||||
@@ -122,10 +120,6 @@ public:
|
||||
aError = SetSearch(aSeach);
|
||||
}
|
||||
|
||||
mozilla::dom::URLSearchParams* SearchParams();
|
||||
|
||||
void SetSearchParams(mozilla::dom::URLSearchParams& aSearchParams);
|
||||
|
||||
void GetHash(nsAString& aHash, ErrorResult& aError)
|
||||
{
|
||||
aError = GetHash(aHash);
|
||||
@@ -144,17 +138,10 @@ public:
|
||||
}
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
// URLSearchParamsObserver
|
||||
void URLSearchParamsUpdated(mozilla::dom::URLSearchParams* aSearchParams) override;
|
||||
|
||||
protected:
|
||||
virtual ~nsLocation();
|
||||
|
||||
nsresult SetSearchInternal(const nsAString& aSearch);
|
||||
void UpdateURLSearchParams();
|
||||
void RemoveURLSearchParams();
|
||||
|
||||
mozilla::dom::URLSearchParams* GetDocShellSearchParams();
|
||||
|
||||
// In the case of jar: uris, we sometimes want the place the jar was
|
||||
// fetched from as the URI instead of the jar: uri itself. Pass in
|
||||
@@ -173,9 +160,7 @@ protected:
|
||||
|
||||
nsString mCachedHash;
|
||||
nsCOMPtr<nsPIDOMWindow> mInnerWindow;
|
||||
nsRefPtr<mozilla::dom::URLSearchParams> mSearchParams;
|
||||
nsWeakPtr mDocShell;
|
||||
};
|
||||
|
||||
#endif // nsLocation_h__
|
||||
|
||||
|
||||
@@ -596,7 +596,7 @@ CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
// Might as well intern, since we're going to need an atomized
|
||||
// version of name anyway when we stick our constructor on the
|
||||
// global.
|
||||
JS::Rooted<JSString*> nameStr(cx, JS_InternString(cx, name));
|
||||
JS::Rooted<JSString*> nameStr(cx, JS_AtomizeAndPinString(cx, name));
|
||||
if (!nameStr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+30
-30
@@ -1726,10 +1726,10 @@ GetCallbackFromCallbackObject(T& aObj)
|
||||
}
|
||||
|
||||
static inline bool
|
||||
InternJSString(JSContext* cx, jsid& jid, const char* chars)
|
||||
AtomizeAndPinJSString(JSContext* cx, jsid& id, const char* chars)
|
||||
{
|
||||
if (JSString *str = ::JS_InternString(cx, chars)) {
|
||||
jid = INTERNED_STRING_TO_JSID(cx, str);
|
||||
if (JSString *str = ::JS_AtomizeAndPinString(cx, chars)) {
|
||||
id = INTERNED_STRING_TO_JSID(cx, str);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -2374,25 +2374,9 @@ inline bool
|
||||
AddStringToIDVector(JSContext* cx, JS::AutoIdVector& vector, const char* name)
|
||||
{
|
||||
return vector.growBy(1) &&
|
||||
InternJSString(cx, *(vector[vector.length() - 1]).address(), name);
|
||||
AtomizeAndPinJSString(cx, *(vector[vector.length() - 1]).address(), name);
|
||||
}
|
||||
|
||||
// We use one constructor JSNative to represent all DOM interface objects (so
|
||||
// we can easily detect when we need to wrap them in an Xray wrapper). We store
|
||||
// the real JSNative in the mNative member of a JSNativeHolder in the
|
||||
// CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT slot of the JSFunction object for a
|
||||
// specific interface object. We also store the NativeProperties in the
|
||||
// JSNativeHolder.
|
||||
// Note that some interface objects are not yet a JSFunction but a normal
|
||||
// JSObject with a DOMJSClass, those do not use these slots.
|
||||
|
||||
enum {
|
||||
CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0
|
||||
};
|
||||
|
||||
bool
|
||||
Constructor(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
// Implementation of the bits that XrayWrapper needs
|
||||
|
||||
/**
|
||||
@@ -2483,6 +2467,22 @@ XrayGetNativeProto(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
|
||||
extern NativePropertyHooks sEmptyNativePropertyHooks;
|
||||
|
||||
// We use one constructor JSNative to represent all DOM interface objects (so
|
||||
// we can easily detect when we need to wrap them in an Xray wrapper). We store
|
||||
// the real JSNative in the mNative member of a JSNativeHolder in the
|
||||
// CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT slot of the JSFunction object for a
|
||||
// specific interface object. We also store the NativeProperties in the
|
||||
// JSNativeHolder.
|
||||
// Note that some interface objects are not yet a JSFunction but a normal
|
||||
// JSObject with a DOMJSClass, those do not use these slots.
|
||||
|
||||
enum {
|
||||
CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0
|
||||
};
|
||||
|
||||
bool
|
||||
Constructor(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
inline bool
|
||||
UseDOMXray(JSObject* obj)
|
||||
{
|
||||
@@ -3065,31 +3065,31 @@ CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
|
||||
}
|
||||
|
||||
/*
|
||||
* Holds a jsid that is initialized to an interned string, with conversion to
|
||||
* Handle<jsid>.
|
||||
* Holds a jsid that is initialized to a pinned string, with automatic
|
||||
* conversion to Handle<jsid>, as it is held live forever by pinning.
|
||||
*/
|
||||
class InternedStringId
|
||||
class PinnedStringId
|
||||
{
|
||||
jsid jid;
|
||||
jsid id;
|
||||
|
||||
public:
|
||||
InternedStringId() : jid(JSID_VOID) {}
|
||||
PinnedStringId() : id(JSID_VOID) {}
|
||||
|
||||
bool init(JSContext *cx, const char *string) {
|
||||
JSString* str = JS_InternString(cx, string);
|
||||
JSString* str = JS_AtomizeAndPinString(cx, string);
|
||||
if (!str)
|
||||
return false;
|
||||
jid = INTERNED_STRING_TO_JSID(cx, str);
|
||||
id = INTERNED_STRING_TO_JSID(cx, str);
|
||||
return true;
|
||||
}
|
||||
|
||||
operator const jsid& () {
|
||||
return jid;
|
||||
return id;
|
||||
}
|
||||
|
||||
operator JS::Handle<jsid> () {
|
||||
/* This is safe because we have interned the string. */
|
||||
return JS::Handle<jsid>::fromMarkedLocation(&jid);
|
||||
/* This is safe because we have pinned the string. */
|
||||
return JS::Handle<jsid>::fromMarkedLocation(&id);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -12236,7 +12236,7 @@ class CGResolveSystemBinding(CGAbstractMethod):
|
||||
jsidInits = CGList(
|
||||
(CGIfWrapper(
|
||||
CGGeneric("return false;\n"),
|
||||
'!InternJSString(aCx, %s, "%s")' %
|
||||
'!AtomizeAndPinJSString(aCx, %s, "%s")' %
|
||||
(descNameToId(desc.name), desc.interface.identifier.name))
|
||||
for desc in descriptors),
|
||||
"\n")
|
||||
@@ -14576,7 +14576,7 @@ class GlobalGenRoots():
|
||||
def memberToAtomCacheMember(binaryNameFor, m):
|
||||
binaryMemberName = binaryNameFor(m.identifier.name)
|
||||
return ClassMember(CGDictionary.makeIdName(binaryMemberName),
|
||||
"InternedStringId", visibility="public")
|
||||
"PinnedStringId", visibility="public")
|
||||
def buildAtomCacheStructure(idlobj, binaryNameFor, members):
|
||||
classMembers = [memberToAtomCacheMember(binaryNameFor, m)
|
||||
for m in members]
|
||||
@@ -14622,7 +14622,7 @@ class GlobalGenRoots():
|
||||
CGWrapper(structs, pre='\n'))
|
||||
curr = CGWrapper(curr, post='\n')
|
||||
|
||||
# Add include statement for InternedStringId.
|
||||
# Add include statement for PinnedStringId.
|
||||
declareIncludes = ['mozilla/dom/BindingUtils.h']
|
||||
curr = CGHeaders([], [], [], [], declareIncludes, [], 'GeneratedAtomList',
|
||||
curr)
|
||||
@@ -14799,7 +14799,7 @@ class GlobalGenRoots():
|
||||
skipGen=False)]
|
||||
defineIncludes.append("nsThreadUtils.h") # For NS_IsMainThread
|
||||
defineIncludes.append("js/Id.h") # For jsid
|
||||
defineIncludes.append("mozilla/dom/BindingUtils.h") # InternJSString
|
||||
defineIncludes.append("mozilla/dom/BindingUtils.h") # AtomizeAndPinJSString
|
||||
|
||||
curr = CGHeaders([], [], [], [], [], defineIncludes,
|
||||
'ResolveSystemBinding', curr)
|
||||
|
||||
@@ -25,7 +25,7 @@ jsid s_length_id = JSID_VOID;
|
||||
bool
|
||||
DefineStaticJSVals(JSContext* cx)
|
||||
{
|
||||
return InternJSString(cx, s_length_id, "length");
|
||||
return AtomizeAndPinJSString(cx, s_length_id, "length");
|
||||
}
|
||||
|
||||
const char DOMProxyHandler::family = 0;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
interface nsIDocument;
|
||||
interface nsIContent;
|
||||
|
||||
[scriptable, uuid(c0716002-5602-4002-a0de-cc69b924b2c6)]
|
||||
[scriptable, uuid(2487F9CA-D05F-4BD1-8F43-5964E746C482)]
|
||||
/**
|
||||
* The focus manager deals with all focus related behaviour. Only one element
|
||||
* in the entire application may have the focus at a time; this element
|
||||
@@ -97,7 +97,8 @@ interface nsIFocusManager : nsISupports
|
||||
* MOVEFOCUS_CARET, then the focus is cleared. If aType is any other value,
|
||||
* the focus is not changed.
|
||||
*
|
||||
* Returns the element that was focused.
|
||||
* Returns the element that was focused. The return value may be null if focus
|
||||
* was moved into a child process.
|
||||
*/
|
||||
nsIDOMElement moveFocus(in nsIDOMWindow aWindow, in nsIDOMElement aStartElement,
|
||||
in unsigned long aType, in unsigned long aFlags);
|
||||
@@ -215,6 +216,11 @@ interface nsIFocusManager : nsISupports
|
||||
*/
|
||||
const unsigned long MOVEFOCUS_CARET = 8;
|
||||
|
||||
/** move focus to the first focusable document */
|
||||
const unsigned long MOVEFOCUS_FIRSTDOC = 9;
|
||||
/** move focus to the last focusable document */
|
||||
const unsigned long MOVEFOCUS_LASTDOC = 10;
|
||||
|
||||
/**
|
||||
* Called when a window has been raised.
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include "domstubs.idl"
|
||||
|
||||
[scriptable, uuid(4d4576eb-ecfe-47b9-93b8-4121518621ad)]
|
||||
[scriptable, uuid(A10D887D-7FC2-48A2-ABC6-A2027423860C)]
|
||||
interface nsITabParent : nsISupports
|
||||
{
|
||||
void injectTouchEvent(in AString aType,
|
||||
@@ -27,5 +27,11 @@ interface nsITabParent : nsISupports
|
||||
|
||||
readonly attribute uint64_t tabId;
|
||||
|
||||
/**
|
||||
* If aForward is true, navigate to the first focusable document.
|
||||
* If aForward is false, navigate to the last focusable document.
|
||||
*/
|
||||
void navigateDocument(in bool aForward);
|
||||
|
||||
readonly attribute boolean hasContentOpener;
|
||||
};
|
||||
|
||||
@@ -142,9 +142,9 @@ parent:
|
||||
parent:
|
||||
/**
|
||||
* When child sends this message, parent should move focus to
|
||||
* the next or previous focusable element.
|
||||
* the next or previous focusable element or document.
|
||||
*/
|
||||
MoveFocus(bool forward);
|
||||
MoveFocus(bool forward, bool forDocumentNavigation);
|
||||
|
||||
Event(RemoteDOMEvent aEvent);
|
||||
|
||||
@@ -696,6 +696,11 @@ child:
|
||||
*/
|
||||
SetIsDocShellActive(bool aIsActive);
|
||||
|
||||
/**
|
||||
* Navigate by document.
|
||||
*/
|
||||
NavigateDocument(bool aForward);
|
||||
|
||||
/**
|
||||
* The parent (chrome thread) requests that the child inform it when
|
||||
* the graphics objects are ready to display.
|
||||
|
||||
+22
-4
@@ -1371,16 +1371,16 @@ TabChild::Blur()
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabChild::FocusNextElement()
|
||||
TabChild::FocusNextElement(bool aForDocumentNavigation)
|
||||
{
|
||||
SendMoveFocus(true);
|
||||
SendMoveFocus(true, aForDocumentNavigation);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabChild::FocusPrevElement()
|
||||
TabChild::FocusPrevElement(bool aForDocumentNavigation)
|
||||
{
|
||||
SendMoveFocus(false);
|
||||
SendMoveFocus(false, aForDocumentNavigation);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -2826,6 +2826,24 @@ TabChild::RecvSetIsDocShellActive(const bool& aIsActive)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TabChild::RecvNavigateDocument(const bool& aForward)
|
||||
{
|
||||
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
if (fm) {
|
||||
nsCOMPtr<nsIDOMElement> result;
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(WebNavigation());
|
||||
|
||||
// Move to the first or last document.
|
||||
fm->MoveFocus(window, nullptr, aForward ? nsIFocusManager::MOVEFOCUS_FIRSTDOC :
|
||||
nsIFocusManager::MOVEFOCUS_LASTDOC,
|
||||
nsIFocusManager::FLAG_BYKEY, getter_AddRefs(result));
|
||||
SendRequestFocus(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PRenderFrameChild*
|
||||
TabChild::AllocPRenderFrameChild()
|
||||
{
|
||||
|
||||
@@ -524,6 +524,7 @@ protected:
|
||||
virtual bool RecvDestroy() override;
|
||||
virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) override;
|
||||
virtual bool RecvSetIsDocShellActive(const bool& aIsActive) override;
|
||||
virtual bool RecvNavigateDocument(const bool& aForward) override;
|
||||
|
||||
virtual bool RecvRequestNotifyAfterRemotePaint() override;
|
||||
|
||||
|
||||
+14
-3
@@ -499,13 +499,17 @@ TabParent::ActorDestroy(ActorDestroyReason why)
|
||||
}
|
||||
|
||||
bool
|
||||
TabParent::RecvMoveFocus(const bool& aForward)
|
||||
TabParent::RecvMoveFocus(const bool& aForward, const bool& aForDocumentNavigation)
|
||||
{
|
||||
nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
|
||||
if (fm) {
|
||||
nsCOMPtr<nsIDOMElement> dummy;
|
||||
uint32_t type = aForward ? uint32_t(nsIFocusManager::MOVEFOCUS_FORWARD)
|
||||
: uint32_t(nsIFocusManager::MOVEFOCUS_BACKWARD);
|
||||
|
||||
uint32_t type = aForward ?
|
||||
(aForDocumentNavigation ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FORWARDDOC) :
|
||||
static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FORWARD)) :
|
||||
(aForDocumentNavigation ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_BACKWARDDOC) :
|
||||
static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_BACKWARD));
|
||||
nsCOMPtr<nsIDOMElement> frame = do_QueryInterface(mFrameElement);
|
||||
fm->MoveFocus(nullptr, frame, type, nsIFocusManager::FLAG_BYKEY,
|
||||
getter_AddRefs(dummy));
|
||||
@@ -3038,6 +3042,13 @@ TabParent::SetHasContentOpener(bool aHasContentOpener)
|
||||
mHasContentOpener = aHasContentOpener;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabParent::NavigateDocument(bool aForward)
|
||||
{
|
||||
unused << SendNavigateDocument(aForward);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class LayerTreeUpdateRunnable final
|
||||
: public nsRunnable
|
||||
{
|
||||
|
||||
+2
-1
@@ -125,7 +125,8 @@ public:
|
||||
void AddWindowListeners();
|
||||
void DidRefresh() override;
|
||||
|
||||
virtual bool RecvMoveFocus(const bool& aForward) override;
|
||||
virtual bool RecvMoveFocus(const bool& aForward,
|
||||
const bool& aForDocumentNavigation) override;
|
||||
virtual bool RecvEvent(const RemoteDOMEvent& aEvent) override;
|
||||
virtual bool RecvReplyKeyEvent(const WidgetKeyboardEvent& aEvent) override;
|
||||
virtual bool RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent) override;
|
||||
|
||||
@@ -1078,7 +1078,7 @@ void AudioCallbackDriver::CompleteAudioContextOperations(AsyncCubebOperation aOp
|
||||
array.SwapElements(mPromisesForOperation);
|
||||
}
|
||||
|
||||
for (int32_t i = array.Length() - 1; i >= 0; i--) {
|
||||
for (uint32_t i = 0; i < array.Length(); i++) {
|
||||
StreamAndPromiseForOperation& s = array[i];
|
||||
if ((aOperation == AsyncCubebOperation::INIT &&
|
||||
s.mOperation == dom::AudioContextOperation::Resume) ||
|
||||
@@ -1089,6 +1089,7 @@ void AudioCallbackDriver::CompleteAudioContextOperations(AsyncCubebOperation aOp
|
||||
s.mPromise,
|
||||
s.mOperation);
|
||||
array.RemoveElementAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<script>
|
||||
(new AudioContext).close();
|
||||
</script>
|
||||
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
|
||||
function boom()
|
||||
{
|
||||
var ac = new AudioContext();
|
||||
// first "suspended" -> "running" transition
|
||||
ac.onstatechange = function() {
|
||||
ac.onstatechange = null;
|
||||
ac.suspend();
|
||||
ac.close();
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="boom();"></body>
|
||||
</html>
|
||||
|
||||
@@ -75,7 +75,10 @@ load offline-buffer-source-ended-1.html
|
||||
load oscillator-ended-1.html
|
||||
load oscillator-ended-2.html
|
||||
load 1080986.html
|
||||
load 1158427.html
|
||||
load 1157994.html
|
||||
load 1122218.html
|
||||
load 1127188.html
|
||||
load 1158427.html
|
||||
load audiocontext-double-suspend.html
|
||||
include ../../mediasource/test/crashtests/crashtests.list
|
||||
|
||||
@@ -761,13 +761,21 @@ private:
|
||||
nsRefPtr<AudioContext> mAudioContext;
|
||||
};
|
||||
|
||||
|
||||
|
||||
void
|
||||
AudioContext::OnStateChanged(void* aPromise, AudioContextState aNewState)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// This can happen if close() was called right after creating the
|
||||
// AudioContext, before the context has switched to "running".
|
||||
if (mAudioContextState == AudioContextState::Closed &&
|
||||
aNewState == AudioContextState::Running &&
|
||||
!aPromise) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef WIN32 // Bug 1170547
|
||||
|
||||
MOZ_ASSERT((mAudioContextState == AudioContextState::Suspended &&
|
||||
aNewState == AudioContextState::Running) ||
|
||||
(mAudioContextState == AudioContextState::Running &&
|
||||
@@ -779,6 +787,8 @@ AudioContext::OnStateChanged(void* aPromise, AudioContextState aNewState)
|
||||
(mAudioContextState == aNewState),
|
||||
"Invalid AudioContextState transition");
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
MOZ_ASSERT(
|
||||
mIsOffline || aPromise || aNewState == AudioContextState::Running,
|
||||
"We should have a promise here if this is a real-time AudioContext."
|
||||
|
||||
@@ -1055,7 +1055,7 @@ nsJSObjWrapper::NP_Enumerate(NPObject* npobj, NPIdentifier** idarray,
|
||||
NPIdentifier id;
|
||||
if (v.isString()) {
|
||||
JS::Rooted<JSString*> str(cx, v.toString());
|
||||
str = JS_InternJSString(cx, str);
|
||||
str = JS_AtomizeAndPinJSString(cx, str);
|
||||
if (!str) {
|
||||
PR_Free(*idarray);
|
||||
return false;
|
||||
@@ -1556,9 +1556,9 @@ CallNPMethodInternal(JSContext* cx, JS::Handle<JSObject*> obj, unsigned argc,
|
||||
// the function object.
|
||||
|
||||
if (npobj->_class->invoke) {
|
||||
JSFunction* fun = ::JS_GetObjectFunction(funobj);
|
||||
JSFunction *fun = ::JS_GetObjectFunction(funobj);
|
||||
JS::Rooted<JSString*> funId(cx, ::JS_GetFunctionId(fun));
|
||||
JSString* name = ::JS_InternJSString(cx, funId);
|
||||
JSString *name = ::JS_AtomizeAndPinJSString(cx, funId);
|
||||
NPIdentifier id = StringToNPIdentifier(cx, name);
|
||||
|
||||
ok = npobj->_class->invoke(npobj, id, npargs, argc, &v);
|
||||
|
||||
@@ -680,7 +680,7 @@ doGetIdentifier(JSContext *cx, const NPUTF8* name)
|
||||
{
|
||||
NS_ConvertUTF8toUTF16 utf16name(name);
|
||||
|
||||
JSString *str = ::JS_InternUCStringN(cx, utf16name.get(), utf16name.Length());
|
||||
JSString *str = ::JS_AtomizeAndPinUCStringN(cx, utf16name.get(), utf16name.Length());
|
||||
|
||||
if (!str)
|
||||
return nullptr;
|
||||
|
||||
@@ -132,7 +132,7 @@ inline bool
|
||||
NPStringIdentifierIsPermanent(NPIdentifier id)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
return JS_StringHasBeenInterned(cx, NPIdentifierToString(id));
|
||||
return JS_StringHasBeenPinned(cx, NPIdentifierToString(id));
|
||||
}
|
||||
|
||||
#define NPIdentifier_VOID (JSIdToNPIdentifier(JSID_VOID))
|
||||
|
||||
@@ -21,7 +21,7 @@ using namespace mozilla::plugins::parent;
|
||||
|
||||
/**
|
||||
* NPIdentifiers in the chrome process are stored as jsids. The difficulty is in
|
||||
* ensuring that string identifiers are rooted without interning them all. We
|
||||
* ensuring that string identifiers are rooted without pinning them all. We
|
||||
* assume that all NPIdentifiers passed into nsJSNPRuntime will not be used
|
||||
* outside the scope of the NPAPI call (i.e., they won't be stored in the
|
||||
* heap). Rooting is done using the StackIdentifier class, which roots the
|
||||
@@ -31,7 +31,7 @@ using namespace mozilla::plugins::parent;
|
||||
* generational or compacting GC. When Firefox implements a moving GC for
|
||||
* strings, we will need to ensure that no movement happens while NPAPI code is
|
||||
* on the stack: although StackIdentifier roots all identifiers used, the GC has
|
||||
* no way to no that a jsid cast to an NPIdentifier needs to be fixed up if it
|
||||
* no way to know that a jsid cast to an NPIdentifier needs to be fixed up if it
|
||||
* is moved.
|
||||
*/
|
||||
|
||||
@@ -39,7 +39,7 @@ class MOZ_STACK_CLASS StackIdentifier
|
||||
{
|
||||
public:
|
||||
explicit StackIdentifier(const PluginIdentifier& aIdentifier,
|
||||
bool aIntern = false);
|
||||
bool aAtomizeAndPin = false);
|
||||
|
||||
bool Failed() const { return mFailed; }
|
||||
NPIdentifier ToNPIdentifier() const { return mIdentifier; }
|
||||
@@ -51,7 +51,7 @@ private:
|
||||
JS::RootedId mId;
|
||||
};
|
||||
|
||||
StackIdentifier::StackIdentifier(const PluginIdentifier& aIdentifier, bool aIntern)
|
||||
StackIdentifier::StackIdentifier(const PluginIdentifier& aIdentifier, bool aAtomizeAndPin)
|
||||
: mFailed(false),
|
||||
mId(mCx)
|
||||
{
|
||||
@@ -64,8 +64,8 @@ StackIdentifier::StackIdentifier(const PluginIdentifier& aIdentifier, bool aInte
|
||||
mFailed = true;
|
||||
return;
|
||||
}
|
||||
if (aIntern) {
|
||||
str = JS_InternJSString(mCx, str);
|
||||
if (aAtomizeAndPin) {
|
||||
str = JS_AtomizeAndPinJSString(mCx, str);
|
||||
if (!str) {
|
||||
NS_ERROR("Id can't be allocated");
|
||||
mFailed = true;
|
||||
@@ -500,9 +500,9 @@ PluginScriptableObjectParent::ScriptableEnumerate(NPObject* aObject,
|
||||
}
|
||||
|
||||
for (uint32_t index = 0; index < *aCount; index++) {
|
||||
// We intern the ID to avoid a GC hazard here. This could probably be fixed
|
||||
// We pin the ID to avoid a GC hazard here. This could probably be fixed
|
||||
// if the interface with nsJSNPRuntime were smarter.
|
||||
StackIdentifier stackID(identifiers[index], true /* aIntern */);
|
||||
StackIdentifier stackID(identifiers[index], true /* aAtomizeAndPin */);
|
||||
if (stackID.Failed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,5 @@ interface Location {
|
||||
[Throws, UnsafeInPrerendering]
|
||||
void reload(optional boolean forceget = false);
|
||||
};
|
||||
// No support for .searchParams on Location yet. See bug 1082734.
|
||||
|
||||
Location implements URLUtils;
|
||||
|
||||
@@ -15,7 +15,7 @@ dictionary ProfileTimelineStackFrame {
|
||||
DOMString functionDisplayName;
|
||||
object? parent = null;
|
||||
object? asyncParent = null;
|
||||
object? asyncCause = null;
|
||||
DOMString asyncCause;
|
||||
};
|
||||
|
||||
dictionary ProfileTimelineLayerRect {
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
* attributes.
|
||||
*
|
||||
* IMPORTANT: If you add any members here, you need to update the
|
||||
* CreateSuffix, Serialize, and Deserialize implementations in BasePrincipal,
|
||||
* and bump the CIDs of all the principal implementations that invoke those
|
||||
* methods.
|
||||
* methods on mozilla::OriginAttributes, and bump the CIDs of all
|
||||
* the principal implementations that use OriginAttributes in their
|
||||
* nsISerializable implementations.
|
||||
*/
|
||||
dictionary OriginAttributesDictionary {
|
||||
unsigned long appId = 0;
|
||||
|
||||
@@ -12,19 +12,21 @@
|
||||
* to the embedding chrome. See mozilla bug #70224 for gratuitous info.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(d2206418-1dd1-11b2-8e55-acddcd2bcfb8)]
|
||||
[scriptable, uuid(947B2EE6-51ED-4C2B-9F45-426C27CA84C6)]
|
||||
interface nsIWebBrowserChromeFocus : nsISupports
|
||||
{
|
||||
/**
|
||||
* Set the focus at the next focusable element in the chrome.
|
||||
* Set the focus at the next focusable element in the chrome. If
|
||||
* aForDocumentNavigation is true, this was a document navigation, so
|
||||
* focus the parent window.
|
||||
*/
|
||||
|
||||
void focusNextElement();
|
||||
void focusNextElement(in bool aForDocumentNavigation);
|
||||
|
||||
/**
|
||||
* Set the focus at the previous focusable element in the chrome.
|
||||
*/
|
||||
|
||||
void focusPrevElement();
|
||||
void focusPrevElement(in bool aForDocumentNavigation);
|
||||
|
||||
};
|
||||
|
||||
+6
-2
@@ -358,12 +358,16 @@ class MOZ_STACK_CLASS JS_PUBLIC_API(AutoEntryMonitor) {
|
||||
// We have begun executing |function|. Note that |function| may not be the
|
||||
// actual closure we are running, but only the canonical function object to
|
||||
// which the script refers.
|
||||
virtual void Entry(JSContext* cx, JSFunction* function) = 0;
|
||||
virtual void Entry(JSContext* cx, JSFunction* function,
|
||||
HandleValue asyncStack,
|
||||
HandleString asyncCause) = 0;
|
||||
|
||||
// Execution has begun at the entry point of |script|, which is not a
|
||||
// function body. (This is probably being executed by 'eval' or some
|
||||
// JSAPI equivalent.)
|
||||
virtual void Entry(JSContext* cx, JSScript* script) = 0;
|
||||
virtual void Entry(JSContext* cx, JSScript* script,
|
||||
HandleValue asyncStack,
|
||||
HandleString asyncCause) = 0;
|
||||
|
||||
// Execution of the function or script has ended.
|
||||
virtual void Exit(JSContext* cx) { }
|
||||
|
||||
@@ -784,7 +784,7 @@ HashableValue::setValue(JSContext* cx, HandleValue v)
|
||||
{
|
||||
if (v.isString()) {
|
||||
// Atomize so that hash() and operator==() are fast and infallible.
|
||||
JSString* str = AtomizeString(cx, v.toString(), DoNotInternAtom);
|
||||
JSString* str = AtomizeString(cx, v.toString(), DoNotPinAtom);
|
||||
if (!str)
|
||||
return false;
|
||||
value = StringValue(str);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
FRAGMENT(jsid, simple) {
|
||||
JS::Rooted<JSString*> string(cx, JS_NewStringCopyZ(cx, "moon"));
|
||||
JS::Rooted<JSString*> interned(cx, JS_InternJSString(cx, string));
|
||||
JS::Rooted<JSString*> interned(cx, JS_AtomizeAndPinJSString(cx, string));
|
||||
JS::Rooted<jsid> string_id(cx, INTERNED_STRING_TO_JSID(cx, interned));
|
||||
jsid int_id = INT_TO_JSID(1729);
|
||||
JS::Rooted<jsid> unique_symbol_id(
|
||||
@@ -38,7 +38,7 @@ jsid_handles(JS::Handle<jsid> jsid_handle,
|
||||
|
||||
FRAGMENT(jsid, handles) {
|
||||
JS::Rooted<JSString*> string(cx, JS_NewStringCopyZ(cx, "shovel"));
|
||||
JS::Rooted<JSString*> interned(cx, JS_InternJSString(cx, string));
|
||||
JS::Rooted<JSString*> interned(cx, JS_AtomizeAndPinJSString(cx, string));
|
||||
JS::Rooted<jsid> string_id(cx, INTERNED_STRING_TO_JSID(cx, interned));
|
||||
jsid_handles(string_id, &string_id);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// |jit-test| --no-baseline; --non-writable-jitcode
|
||||
(function(stdlib, foreign, heap) {
|
||||
"use asm";
|
||||
function f() {}
|
||||
return f;
|
||||
})();
|
||||
@@ -48,6 +48,28 @@ ExecutablePool::~ExecutablePool()
|
||||
m_allocator->releasePoolPages(this);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ExecutableAllocator::initStatic()
|
||||
{
|
||||
if (!pageSize) {
|
||||
pageSize = determinePageSize();
|
||||
// On Windows, VirtualAlloc effectively allocates in 64K chunks.
|
||||
// (Technically, it allocates in page chunks, but the starting
|
||||
// address is always a multiple of 64K, so each allocation uses up
|
||||
// 64K of address space.) So a size less than that would be
|
||||
// pointless. But it turns out that 64KB is a reasonable size for
|
||||
// all platforms. (This assumes 4KB pages.) On 64-bit windows,
|
||||
// AllocateExecutableMemory prepends an extra page for structured
|
||||
// exception handling data (see comments in function) onto whatever
|
||||
// is passed in, so subtract one page here.
|
||||
#if defined(JS_CPU_X64) && defined(XP_WIN)
|
||||
largeAllocSize = pageSize * 15;
|
||||
#else
|
||||
largeAllocSize = pageSize * 16;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExecutableAllocator::addSizeOfCode(JS::CodeSizes* sizes) const
|
||||
{
|
||||
|
||||
@@ -182,24 +182,6 @@ class ExecutableAllocator
|
||||
ExecutableAllocator()
|
||||
: destroyCallback(nullptr)
|
||||
{
|
||||
if (!pageSize) {
|
||||
pageSize = determinePageSize();
|
||||
// On Windows, VirtualAlloc effectively allocates in 64K chunks.
|
||||
// (Technically, it allocates in page chunks, but the starting
|
||||
// address is always a multiple of 64K, so each allocation uses up
|
||||
// 64K of address space.) So a size less than that would be
|
||||
// pointless. But it turns out that 64KB is a reasonable size for
|
||||
// all platforms. (This assumes 4KB pages.) On 64-bit windows,
|
||||
// AllocateExecutableMemory prepends an extra page for structured
|
||||
// exception handling data (see comments in function) onto whatever
|
||||
// is passed in, so subtract one page here.
|
||||
#if defined(JS_CPU_X64) && defined(XP_WIN)
|
||||
largeAllocSize = pageSize * 15;
|
||||
#else
|
||||
largeAllocSize = pageSize * 16;
|
||||
#endif
|
||||
}
|
||||
|
||||
MOZ_ASSERT(m_smallPools.empty());
|
||||
}
|
||||
|
||||
@@ -263,6 +245,8 @@ class ExecutableAllocator
|
||||
this->destroyCallback = destroyCallback;
|
||||
}
|
||||
|
||||
static void initStatic();
|
||||
|
||||
static bool nonWritableJitCode;
|
||||
|
||||
private:
|
||||
|
||||
@@ -12,7 +12,6 @@ UNIFIED_SOURCES += [
|
||||
'testArgumentsObject.cpp',
|
||||
'testArrayBuffer.cpp',
|
||||
'testArrayBufferView.cpp',
|
||||
'testBindCallable.cpp',
|
||||
'testBug604087.cpp',
|
||||
'testCallNonGenericMethodOnProxy.cpp',
|
||||
'testChromeBuffer.cpp',
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/* 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 "jsapi-tests/tests.h"
|
||||
|
||||
BEGIN_TEST(test_BindCallable)
|
||||
{
|
||||
JS::RootedValue v(cx);
|
||||
EVAL("({ somename : 1717 })", &v);
|
||||
CHECK(v.isObject());
|
||||
|
||||
JS::RootedValue func(cx);
|
||||
EVAL("(function() { return this.somename; })", &func);
|
||||
CHECK(func.isObject());
|
||||
|
||||
JS::RootedObject funcObj(cx, func.toObjectOrNull());
|
||||
JS::RootedObject vObj(cx, v.toObjectOrNull());
|
||||
JSObject* newCallable = JS_BindCallable(cx, funcObj, vObj);
|
||||
CHECK(newCallable);
|
||||
|
||||
JS::RootedValue retval(cx);
|
||||
JS::RootedValue fun(cx, JS::ObjectValue(*newCallable));
|
||||
bool called = JS_CallFunctionValue(cx, nullptr, fun, JS::HandleValueArray::empty(), &retval);
|
||||
CHECK(called);
|
||||
|
||||
CHECK(retval.isInt32());
|
||||
|
||||
CHECK(retval.toInt32() == 1717);
|
||||
return true;
|
||||
}
|
||||
END_TEST(test_BindCallable)
|
||||
@@ -13,28 +13,28 @@ BEGIN_TEST(testIntString_bug515273)
|
||||
|
||||
EVAL("'1';", &v);
|
||||
JSString* str = v.toString();
|
||||
CHECK(JS_StringHasBeenInterned(cx, str));
|
||||
CHECK(JS_StringHasBeenPinned(cx, str));
|
||||
CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "1"));
|
||||
|
||||
EVAL("'42';", &v);
|
||||
str = v.toString();
|
||||
CHECK(JS_StringHasBeenInterned(cx, str));
|
||||
CHECK(JS_StringHasBeenPinned(cx, str));
|
||||
CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "42"));
|
||||
|
||||
EVAL("'111';", &v);
|
||||
str = v.toString();
|
||||
CHECK(JS_StringHasBeenInterned(cx, str));
|
||||
CHECK(JS_StringHasBeenPinned(cx, str));
|
||||
CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "111"));
|
||||
|
||||
/* Test other types of static strings. */
|
||||
EVAL("'a';", &v);
|
||||
str = v.toString();
|
||||
CHECK(JS_StringHasBeenInterned(cx, str));
|
||||
CHECK(JS_StringHasBeenPinned(cx, str));
|
||||
CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "a"));
|
||||
|
||||
EVAL("'bc';", &v);
|
||||
str = v.toString();
|
||||
CHECK(JS_StringHasBeenInterned(cx, str));
|
||||
CHECK(JS_StringHasBeenPinned(cx, str));
|
||||
CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "bc"));
|
||||
|
||||
return true;
|
||||
|
||||
@@ -10,17 +10,17 @@
|
||||
|
||||
using mozilla::ArrayLength;
|
||||
|
||||
BEGIN_TEST(testAtomizedIsNotInterned)
|
||||
BEGIN_TEST(testAtomizedIsNotPinned)
|
||||
{
|
||||
/* Try to pick a string that won't be interned by other tests in this runtime. */
|
||||
static const char someChars[] = "blah blah blah? blah blah blah";
|
||||
JS::Rooted<JSAtom*> atom(cx, js::Atomize(cx, someChars, ArrayLength(someChars)));
|
||||
CHECK(!JS_StringHasBeenInterned(cx, atom));
|
||||
CHECK(JS_InternJSString(cx, atom));
|
||||
CHECK(JS_StringHasBeenInterned(cx, atom));
|
||||
CHECK(!JS_StringHasBeenPinned(cx, atom));
|
||||
CHECK(JS_AtomizeAndPinJSString(cx, atom));
|
||||
CHECK(JS_StringHasBeenPinned(cx, atom));
|
||||
return true;
|
||||
}
|
||||
END_TEST(testAtomizedIsNotInterned)
|
||||
END_TEST(testAtomizedIsNotPinned)
|
||||
|
||||
struct StringWrapperStruct
|
||||
{
|
||||
@@ -28,9 +28,9 @@ struct StringWrapperStruct
|
||||
bool strOk;
|
||||
} sw;
|
||||
|
||||
BEGIN_TEST(testInternAcrossGC)
|
||||
BEGIN_TEST(testPinAcrossGC)
|
||||
{
|
||||
sw.str = JS_InternString(cx, "wrapped chars that another test shouldn't be using");
|
||||
sw.str = JS_AtomizeAndPinString(cx, "wrapped chars that another test shouldn't be using");
|
||||
sw.strOk = false;
|
||||
CHECK(sw.str);
|
||||
JS_AddFinalizeCallback(rt, FinalizeCallback, nullptr);
|
||||
@@ -45,4 +45,4 @@ FinalizeCallback(JSFreeOp* fop, JSFinalizeStatus status, bool isCompartmentGC, v
|
||||
if (status == JSFINALIZE_GROUP_START)
|
||||
sw.strOk = js::gc::IsMarkedUnbarriered(&sw.str);
|
||||
}
|
||||
END_TEST(testInternAcrossGC)
|
||||
END_TEST(testPinAcrossGC)
|
||||
|
||||
+39
-27
@@ -92,6 +92,7 @@
|
||||
|
||||
#include "vm/Interpreter-inl.h"
|
||||
#include "vm/NativeObject-inl.h"
|
||||
#include "vm/SavedStacks-inl.h"
|
||||
#include "vm/String-inl.h"
|
||||
|
||||
using namespace js;
|
||||
@@ -552,6 +553,8 @@ JS_Init(void)
|
||||
if (!TlsPerThreadData.initialized() && !TlsPerThreadData.init())
|
||||
return false;
|
||||
|
||||
jit::ExecutableAllocator::initStatic();
|
||||
|
||||
if (!jit::InitializeIon())
|
||||
return false;
|
||||
|
||||
@@ -988,7 +991,7 @@ JS_GetCompartmentPrivate(JSCompartment* compartment)
|
||||
JS_PUBLIC_API(JSAddonId*)
|
||||
JS::NewAddonId(JSContext* cx, HandleString str)
|
||||
{
|
||||
return static_cast<JSAddonId*>(JS_InternJSString(cx, str));
|
||||
return static_cast<JSAddonId*>(JS_AtomizeAndPinJSString(cx, str));
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSString*)
|
||||
@@ -2760,13 +2763,13 @@ PropertySpecNameToSymbolCode(const char* name)
|
||||
|
||||
static bool
|
||||
PropertySpecNameToId(JSContext* cx, const char* name, MutableHandleId id,
|
||||
js::InternBehavior ib = js::DoNotInternAtom)
|
||||
js::PinningBehavior pin = js::DoNotPinAtom)
|
||||
{
|
||||
if (JS::PropertySpecNameIsSymbol(name)) {
|
||||
JS::SymbolCode which = PropertySpecNameToSymbolCode(name);
|
||||
id.set(SYMBOL_TO_JSID(cx->wellKnownSymbols().get(which)));
|
||||
} else {
|
||||
JSAtom* atom = Atomize(cx, name, strlen(name), ib);
|
||||
JSAtom* atom = Atomize(cx, name, strlen(name), pin);
|
||||
if (!atom)
|
||||
return false;
|
||||
id.set(AtomToId(atom));
|
||||
@@ -2782,7 +2785,7 @@ JS::PropertySpecNameToPermanentId(JSContext* cx, const char* name, jsid* idp)
|
||||
// of this API is to populate *idp with a jsid that does not need to be
|
||||
// marked.
|
||||
return PropertySpecNameToId(cx, name, MutableHandleId::fromMarkedLocation(idp),
|
||||
js::InternAtom);
|
||||
js::PinAtom);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
@@ -3492,13 +3495,6 @@ JS_IsConstructor(JSFunction* fun)
|
||||
return fun->isConstructor();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject*)
|
||||
JS_BindCallable(JSContext* cx, HandleObject target, HandleObject newThis)
|
||||
{
|
||||
RootedValue thisArg(cx, ObjectValue(*newThis));
|
||||
return fun_bind(cx, target, thisArg, nullptr, 0);
|
||||
}
|
||||
|
||||
static bool
|
||||
GenericNativeMethodDispatcher(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@@ -4770,7 +4766,7 @@ JS_NewStringCopyZ(JSContext* cx, const char* s)
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_StringHasBeenInterned(JSContext* cx, JSString* str)
|
||||
JS_StringHasBeenPinned(JSContext* cx, JSString* str)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
@@ -4778,7 +4774,7 @@ JS_StringHasBeenInterned(JSContext* cx, JSString* str)
|
||||
if (!str->isAtom())
|
||||
return false;
|
||||
|
||||
return AtomIsInterned(cx, &str->asAtom());
|
||||
return AtomIsPinned(cx, &str->asAtom());
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(jsid)
|
||||
@@ -4786,33 +4782,33 @@ INTERNED_STRING_TO_JSID(JSContext* cx, JSString* str)
|
||||
{
|
||||
MOZ_ASSERT(str);
|
||||
MOZ_ASSERT(((size_t)str & JSID_TYPE_MASK) == 0);
|
||||
MOZ_ASSERT_IF(cx, JS_StringHasBeenInterned(cx, str));
|
||||
MOZ_ASSERT_IF(cx, JS_StringHasBeenPinned(cx, str));
|
||||
return AtomToId(&str->asAtom());
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSString*)
|
||||
JS_InternJSString(JSContext* cx, HandleString str)
|
||||
JS_AtomizeAndPinJSString(JSContext* cx, HandleString str)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
JSAtom* atom = AtomizeString(cx, str, InternAtom);
|
||||
MOZ_ASSERT_IF(atom, JS_StringHasBeenInterned(cx, atom));
|
||||
JSAtom* atom = AtomizeString(cx, str, PinAtom);
|
||||
MOZ_ASSERT_IF(atom, JS_StringHasBeenPinned(cx, atom));
|
||||
return atom;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSString*)
|
||||
JS_InternString(JSContext* cx, const char* s)
|
||||
JS_AtomizeAndPinString(JSContext* cx, const char* s)
|
||||
{
|
||||
return JS_InternStringN(cx, s, strlen(s));
|
||||
return JS_AtomizeAndPinStringN(cx, s, strlen(s));
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSString*)
|
||||
JS_InternStringN(JSContext* cx, const char* s, size_t length)
|
||||
JS_AtomizeAndPinStringN(JSContext* cx, const char* s, size_t length)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
JSAtom* atom = Atomize(cx, s, length, InternAtom);
|
||||
MOZ_ASSERT_IF(atom, JS_StringHasBeenInterned(cx, atom));
|
||||
JSAtom* atom = Atomize(cx, s, length, PinAtom);
|
||||
MOZ_ASSERT_IF(atom, JS_StringHasBeenPinned(cx, atom));
|
||||
return atom;
|
||||
}
|
||||
|
||||
@@ -4845,19 +4841,19 @@ JS_NewUCStringCopyZ(JSContext* cx, const char16_t* s)
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSString*)
|
||||
JS_InternUCStringN(JSContext* cx, const char16_t* s, size_t length)
|
||||
JS_AtomizeAndPinUCStringN(JSContext* cx, const char16_t* s, size_t length)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
JSAtom* atom = AtomizeChars(cx, s, length, InternAtom);
|
||||
MOZ_ASSERT_IF(atom, JS_StringHasBeenInterned(cx, atom));
|
||||
JSAtom* atom = AtomizeChars(cx, s, length, PinAtom);
|
||||
MOZ_ASSERT_IF(atom, JS_StringHasBeenPinned(cx, atom));
|
||||
return atom;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSString*)
|
||||
JS_InternUCString(JSContext* cx, const char16_t* s)
|
||||
JS_AtomizeAndPinUCString(JSContext* cx, const char16_t* s)
|
||||
{
|
||||
return JS_InternUCStringN(cx, s, js_strlen(s));
|
||||
return JS_AtomizeAndPinUCStringN(cx, s, js_strlen(s));
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(size_t)
|
||||
@@ -6273,6 +6269,22 @@ JS::CaptureCurrentStack(JSContext* cx, JS::MutableHandleObject stackp, unsigned
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::CopyAsyncStack(JSContext* cx, JS::HandleObject asyncStack,
|
||||
JS::HandleString asyncCause, JS::MutableHandleObject stackp,
|
||||
unsigned maxFrameCount)
|
||||
{
|
||||
js::AssertObjectIsSavedFrameOrWrapper(cx, asyncStack);
|
||||
JSCompartment* compartment = cx->compartment();
|
||||
MOZ_ASSERT(compartment);
|
||||
Rooted<SavedFrame*> frame(cx);
|
||||
if (!compartment->savedStacks().copyAsyncStack(cx, asyncStack, asyncCause,
|
||||
&frame, maxFrameCount))
|
||||
return false;
|
||||
stackp.set(frame.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(Zone*)
|
||||
JS::GetObjectZone(JSObject* obj)
|
||||
{
|
||||
|
||||
+27
-23
@@ -739,7 +739,7 @@ JS_NumberValue(double d)
|
||||
/************************************************************************/
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_StringHasBeenInterned(JSContext* cx, JSString* str);
|
||||
JS_StringHasBeenPinned(JSContext* cx, JSString* str);
|
||||
|
||||
namespace JS {
|
||||
|
||||
@@ -3261,14 +3261,6 @@ JS_IsNativeFunction(JSObject* funobj, JSNative call);
|
||||
extern JS_PUBLIC_API(bool)
|
||||
JS_IsConstructor(JSFunction* fun);
|
||||
|
||||
/*
|
||||
* Bind the given callable to use the given object as "this".
|
||||
*
|
||||
* If |callable| is not callable, will throw and return nullptr.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
JS_BindCallable(JSContext* cx, JS::Handle<JSObject*> callable, JS::Handle<JSObject*> newThis);
|
||||
|
||||
// This enum is used to select if properties with JSPROP_DEFINE_LATE flag
|
||||
// should be defined on the object.
|
||||
// Normal JSAPI consumers probably always want DefineAllProperties here.
|
||||
@@ -4049,13 +4041,13 @@ extern JS_PUBLIC_API(JSString*)
|
||||
JS_NewStringCopyZ(JSContext* cx, const char* s);
|
||||
|
||||
extern JS_PUBLIC_API(JSString*)
|
||||
JS_InternJSString(JSContext* cx, JS::HandleString str);
|
||||
JS_AtomizeAndPinJSString(JSContext* cx, JS::HandleString str);
|
||||
|
||||
extern JS_PUBLIC_API(JSString*)
|
||||
JS_InternStringN(JSContext* cx, const char* s, size_t length);
|
||||
JS_AtomizeAndPinStringN(JSContext* cx, const char* s, size_t length);
|
||||
|
||||
extern JS_PUBLIC_API(JSString*)
|
||||
JS_InternString(JSContext* cx, const char* s);
|
||||
JS_AtomizeAndPinString(JSContext* cx, const char* s);
|
||||
|
||||
extern JS_PUBLIC_API(JSString*)
|
||||
JS_NewUCString(JSContext* cx, char16_t* chars, size_t length);
|
||||
@@ -4067,10 +4059,10 @@ extern JS_PUBLIC_API(JSString*)
|
||||
JS_NewUCStringCopyZ(JSContext* cx, const char16_t* s);
|
||||
|
||||
extern JS_PUBLIC_API(JSString*)
|
||||
JS_InternUCStringN(JSContext* cx, const char16_t* s, size_t length);
|
||||
JS_AtomizeAndPinUCStringN(JSContext* cx, const char16_t* s, size_t length);
|
||||
|
||||
extern JS_PUBLIC_API(JSString*)
|
||||
JS_InternUCString(JSContext* cx, const char16_t* s);
|
||||
JS_AtomizeAndPinUCString(JSContext* cx, const char16_t* s);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
JS_CompareStrings(JSContext* cx, JSString* str1, JSString* str2, int32_t* result);
|
||||
@@ -4091,9 +4083,8 @@ JS_FileEscapedString(FILE* fp, JSString* str, char quote);
|
||||
* fail. As indicated by the lack of a JSContext parameter, there are two
|
||||
* special cases where getting the chars is infallible:
|
||||
*
|
||||
* The first case is interned strings, i.e., strings from JS_InternString or
|
||||
* JSID_TO_STRING(id), using JS_GetLatin1InternedStringChars or
|
||||
* JS_GetTwoByteInternedStringChars.
|
||||
* The first case is for strings that have been atomized, e.g. directly by
|
||||
* JS_AtomizeAndPinString or implicitly because it is stored in a jsid.
|
||||
*
|
||||
* The second case is "flat" strings that have been explicitly prepared in a
|
||||
* fallible context by JS_FlattenString. To catch errors, a separate opaque
|
||||
@@ -4154,12 +4145,6 @@ JS_GetTwoByteExternalStringChars(JSString* str);
|
||||
extern JS_PUBLIC_API(bool)
|
||||
JS_CopyStringChars(JSContext* cx, mozilla::Range<char16_t> dest, JSString* str);
|
||||
|
||||
extern JS_PUBLIC_API(const JS::Latin1Char*)
|
||||
JS_GetLatin1InternedStringChars(const JS::AutoCheckCannotGC& nogc, JSString* str);
|
||||
|
||||
extern JS_PUBLIC_API(const char16_t*)
|
||||
JS_GetTwoByteInternedStringChars(const JS::AutoCheckCannotGC& nogc, JSString* str);
|
||||
|
||||
extern JS_PUBLIC_API(JSFlatString*)
|
||||
JS_FlattenString(JSContext* cx, JSString* str);
|
||||
|
||||
@@ -5310,6 +5295,25 @@ SetOutOfMemoryCallback(JSRuntime* rt, OutOfMemoryCallback cb, void* data);
|
||||
extern JS_PUBLIC_API(bool)
|
||||
CaptureCurrentStack(JSContext* cx, MutableHandleObject stackp, unsigned maxFrameCount = 0);
|
||||
|
||||
/*
|
||||
* This is a utility function for preparing an async stack to be used
|
||||
* by some other object. This may be used when you need to treat a
|
||||
* given stack trace as an async parent. If you just need to capture
|
||||
* the current stack, async parents and all, use CaptureCurrentStack
|
||||
* instead.
|
||||
*
|
||||
* Here |asyncStack| is the async stack to prepare. It is copied into
|
||||
* |cx|'s current compartment, and the newest frame is given
|
||||
* |asyncCause| as its asynchronous cause. If |maxFrameCount| is
|
||||
* non-zero, capture at most the youngest |maxFrameCount| frames. The
|
||||
* new stack object is written to |stackp|. Returns true on success,
|
||||
* or sets an exception and returns |false| on error.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
CopyAsyncStack(JSContext* cx, HandleObject asyncStack,
|
||||
HandleString asyncCause, MutableHandleObject stackp,
|
||||
unsigned maxFrameCount);
|
||||
|
||||
/*
|
||||
* Accessors for working with SavedFrame JSObjects
|
||||
*
|
||||
|
||||
+23
-23
@@ -143,7 +143,7 @@ JSRuntime::initializeAtoms(JSContext* cx)
|
||||
|
||||
ImmutablePropertyNamePtr* names = reinterpret_cast<ImmutablePropertyNamePtr*>(commonNames);
|
||||
for (size_t i = 0; i < ArrayLength(cachedNames); i++, names++) {
|
||||
JSAtom* atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, InternAtom);
|
||||
JSAtom* atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, PinAtom);
|
||||
if (!atom)
|
||||
return false;
|
||||
names->init(atom->asPropertyName());
|
||||
@@ -197,11 +197,11 @@ js::MarkAtoms(JSTracer* trc)
|
||||
JSRuntime* rt = trc->runtime();
|
||||
for (AtomSet::Enum e(rt->atoms()); !e.empty(); e.popFront()) {
|
||||
const AtomStateEntry& entry = e.front();
|
||||
if (!entry.isTagged())
|
||||
if (!entry.isPinned())
|
||||
continue;
|
||||
|
||||
JSAtom* atom = entry.asPtr();
|
||||
bool tagged = entry.isTagged();
|
||||
bool tagged = entry.isPinned();
|
||||
TraceRoot(trc, &atom, "interned_atom");
|
||||
if (entry.asPtr() != atom)
|
||||
e.rekeyFront(AtomHasher::Lookup(atom), AtomStateEntry(atom, tagged));
|
||||
@@ -257,7 +257,7 @@ JSRuntime::sweepAtoms()
|
||||
bool isDying = IsAboutToBeFinalizedUnbarriered(&atom);
|
||||
|
||||
/* Pinned or interned key cannot be finalized. */
|
||||
MOZ_ASSERT_IF(hasContexts() && entry.isTagged(), !isDying);
|
||||
MOZ_ASSERT_IF(hasContexts() && entry.isPinned(), !isDying);
|
||||
|
||||
if (isDying)
|
||||
e.removeFront();
|
||||
@@ -289,7 +289,7 @@ JSRuntime::transformToPermanentAtoms(JSContext *cx)
|
||||
}
|
||||
|
||||
bool
|
||||
AtomIsInterned(JSContext* cx, JSAtom* atom)
|
||||
AtomIsPinned(JSContext* cx, JSAtom* atom)
|
||||
{
|
||||
/* We treat static strings as interned because they're never collected. */
|
||||
if (StaticStrings::isStatic(atom))
|
||||
@@ -309,14 +309,14 @@ AtomIsInterned(JSContext* cx, JSAtom* atom)
|
||||
if (!p)
|
||||
return false;
|
||||
|
||||
return p->isTagged();
|
||||
return p->isPinned();
|
||||
}
|
||||
|
||||
/* |tbchars| must not point into an inline or short string. */
|
||||
template <typename CharT>
|
||||
MOZ_ALWAYS_INLINE
|
||||
static JSAtom*
|
||||
AtomizeAndCopyChars(ExclusiveContext* cx, const CharT* tbchars, size_t length, InternBehavior ib)
|
||||
AtomizeAndCopyChars(ExclusiveContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin)
|
||||
{
|
||||
if (JSAtom* s = cx->staticStrings().lookup(tbchars, length))
|
||||
return s;
|
||||
@@ -340,7 +340,7 @@ AtomizeAndCopyChars(ExclusiveContext* cx, const CharT* tbchars, size_t length, I
|
||||
AtomSet::AddPtr p = atoms.lookupForAdd(lookup);
|
||||
if (p) {
|
||||
JSAtom* atom = p->asPtr();
|
||||
p->setTagged(bool(ib));
|
||||
p->setPinned(bool(pin));
|
||||
return atom;
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ AtomizeAndCopyChars(ExclusiveContext* cx, const CharT* tbchars, size_t length, I
|
||||
// We have held the lock since looking up p, and the operations we've done
|
||||
// since then can't GC; therefore the atoms table has not been modified and
|
||||
// p is still valid.
|
||||
if (!atoms.add(p, AtomStateEntry(atom, bool(ib)))) {
|
||||
if (!atoms.add(p, AtomStateEntry(atom, bool(pin)))) {
|
||||
ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
|
||||
return nullptr;
|
||||
}
|
||||
@@ -369,19 +369,19 @@ AtomizeAndCopyChars(ExclusiveContext* cx, const CharT* tbchars, size_t length, I
|
||||
}
|
||||
|
||||
template JSAtom*
|
||||
AtomizeAndCopyChars(ExclusiveContext* cx, const char16_t* tbchars, size_t length, InternBehavior ib);
|
||||
AtomizeAndCopyChars(ExclusiveContext* cx, const char16_t* tbchars, size_t length, PinningBehavior pin);
|
||||
|
||||
template JSAtom*
|
||||
AtomizeAndCopyChars(ExclusiveContext* cx, const Latin1Char* tbchars, size_t length, InternBehavior ib);
|
||||
AtomizeAndCopyChars(ExclusiveContext* cx, const Latin1Char* tbchars, size_t length, PinningBehavior pin);
|
||||
|
||||
JSAtom*
|
||||
js::AtomizeString(ExclusiveContext* cx, JSString* str,
|
||||
js::InternBehavior ib /* = js::DoNotInternAtom */)
|
||||
js::PinningBehavior pin /* = js::DoNotPinAtom */)
|
||||
{
|
||||
if (str->isAtom()) {
|
||||
JSAtom& atom = str->asAtom();
|
||||
/* N.B. static atoms are effectively always interned. */
|
||||
if (ib != InternAtom || js::StaticStrings::isStatic(&atom))
|
||||
if (pin != PinAtom || js::StaticStrings::isStatic(&atom))
|
||||
return &atom;
|
||||
|
||||
AtomHasher::Lookup lookup(&atom);
|
||||
@@ -397,8 +397,8 @@ js::AtomizeString(ExclusiveContext* cx, JSString* str,
|
||||
p = cx->atoms().lookup(lookup);
|
||||
MOZ_ASSERT(p); /* Non-static atom must exist in atom state set. */
|
||||
MOZ_ASSERT(p->asPtr() == &atom);
|
||||
MOZ_ASSERT(ib == InternAtom);
|
||||
p->setTagged(bool(ib));
|
||||
MOZ_ASSERT(pin == PinAtom);
|
||||
p->setPinned(bool(pin));
|
||||
return &atom;
|
||||
}
|
||||
|
||||
@@ -408,12 +408,12 @@ js::AtomizeString(ExclusiveContext* cx, JSString* str,
|
||||
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
return linear->hasLatin1Chars()
|
||||
? AtomizeAndCopyChars(cx, linear->latin1Chars(nogc), linear->length(), ib)
|
||||
: AtomizeAndCopyChars(cx, linear->twoByteChars(nogc), linear->length(), ib);
|
||||
? AtomizeAndCopyChars(cx, linear->latin1Chars(nogc), linear->length(), pin)
|
||||
: AtomizeAndCopyChars(cx, linear->twoByteChars(nogc), linear->length(), pin);
|
||||
}
|
||||
|
||||
JSAtom*
|
||||
js::Atomize(ExclusiveContext* cx, const char* bytes, size_t length, InternBehavior ib)
|
||||
js::Atomize(ExclusiveContext* cx, const char* bytes, size_t length, PinningBehavior pin)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
@@ -421,26 +421,26 @@ js::Atomize(ExclusiveContext* cx, const char* bytes, size_t length, InternBehavi
|
||||
return nullptr;
|
||||
|
||||
const Latin1Char* chars = reinterpret_cast<const Latin1Char*>(bytes);
|
||||
return AtomizeAndCopyChars(cx, chars, length, ib);
|
||||
return AtomizeAndCopyChars(cx, chars, length, pin);
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
JSAtom*
|
||||
js::AtomizeChars(ExclusiveContext* cx, const CharT* chars, size_t length, InternBehavior ib)
|
||||
js::AtomizeChars(ExclusiveContext* cx, const CharT* chars, size_t length, PinningBehavior pin)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
if (!JSString::validateLength(cx, length))
|
||||
return nullptr;
|
||||
|
||||
return AtomizeAndCopyChars(cx, chars, length, ib);
|
||||
return AtomizeAndCopyChars(cx, chars, length, pin);
|
||||
}
|
||||
|
||||
template JSAtom*
|
||||
js::AtomizeChars(ExclusiveContext* cx, const Latin1Char* chars, size_t length, InternBehavior ib);
|
||||
js::AtomizeChars(ExclusiveContext* cx, const Latin1Char* chars, size_t length, PinningBehavior pin);
|
||||
|
||||
template JSAtom*
|
||||
js::AtomizeChars(ExclusiveContext* cx, const char16_t* chars, size_t length, InternBehavior ib);
|
||||
js::AtomizeChars(ExclusiveContext* cx, const char16_t* chars, size_t length, PinningBehavior pin);
|
||||
|
||||
bool
|
||||
js::IndexToIdSlow(ExclusiveContext* cx, uint32_t index, MutableHandleId idp)
|
||||
|
||||
+10
-10
@@ -72,7 +72,7 @@ class AtomStateEntry
|
||||
MOZ_ASSERT((uintptr_t(ptr) & 0x1) == 0);
|
||||
}
|
||||
|
||||
bool isTagged() const {
|
||||
bool isPinned() const {
|
||||
return bits & 0x1;
|
||||
}
|
||||
|
||||
@@ -80,8 +80,8 @@ class AtomStateEntry
|
||||
* Non-branching code sequence. Note that the const_cast is safe because
|
||||
* the hash function doesn't consider the tag to be a portion of the key.
|
||||
*/
|
||||
void setTagged(bool enabled) const {
|
||||
const_cast<AtomStateEntry*>(this)->bits |= uintptr_t(enabled);
|
||||
void setPinned(bool pinned) const {
|
||||
const_cast<AtomStateEntry*>(this)->bits |= uintptr_t(pinned);
|
||||
}
|
||||
|
||||
JSAtom* asPtr() const;
|
||||
@@ -151,7 +151,7 @@ class PropertyName;
|
||||
} /* namespace js */
|
||||
|
||||
extern bool
|
||||
AtomIsInterned(JSContext* cx, JSAtom* atom);
|
||||
AtomIsPinned(JSContext* cx, JSAtom* atom);
|
||||
|
||||
/* Well-known predefined C strings. */
|
||||
#define DECLARE_PROTO_STR(name,code,init,clasp) extern const char js_##name##_str[];
|
||||
@@ -218,23 +218,23 @@ void
|
||||
MarkWellKnownSymbols(JSTracer* trc);
|
||||
|
||||
/* N.B. must correspond to boolean tagging behavior. */
|
||||
enum InternBehavior
|
||||
enum PinningBehavior
|
||||
{
|
||||
DoNotInternAtom = false,
|
||||
InternAtom = true
|
||||
DoNotPinAtom = false,
|
||||
PinAtom = true
|
||||
};
|
||||
|
||||
extern JSAtom*
|
||||
Atomize(ExclusiveContext* cx, const char* bytes, size_t length,
|
||||
js::InternBehavior ib = js::DoNotInternAtom);
|
||||
js::PinningBehavior pin = js::DoNotPinAtom);
|
||||
|
||||
template <typename CharT>
|
||||
extern JSAtom*
|
||||
AtomizeChars(ExclusiveContext* cx, const CharT* chars, size_t length,
|
||||
js::InternBehavior ib = js::DoNotInternAtom);
|
||||
js::PinningBehavior pin = js::DoNotPinAtom);
|
||||
|
||||
extern JSAtom*
|
||||
AtomizeString(ExclusiveContext* cx, JSString* str, js::InternBehavior ib = js::DoNotInternAtom);
|
||||
AtomizeString(ExclusiveContext* cx, JSString* str, js::PinningBehavior pin = js::DoNotPinAtom);
|
||||
|
||||
template <AllowGC allowGC>
|
||||
extern JSAtom*
|
||||
|
||||
+14
-25
@@ -1675,42 +1675,29 @@ js::fun_bind(JSContext* cx, unsigned argc, Value* vp)
|
||||
argslen = args.length() - 1;
|
||||
}
|
||||
|
||||
// Steps 4-14.
|
||||
RootedValue thisArg(cx, args.length() >= 1 ? args[0] : UndefinedValue());
|
||||
RootedObject target(cx, &thisv.toObject());
|
||||
JSObject* boundFunction = fun_bind(cx, target, thisArg, boundArgs, argslen);
|
||||
if (!boundFunction)
|
||||
return false;
|
||||
|
||||
// Step 15.
|
||||
args.rval().setObject(*boundFunction);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::fun_bind(JSContext* cx, HandleObject target, HandleValue thisArg,
|
||||
Value* boundArgs, unsigned argslen)
|
||||
{
|
||||
double length = 0.0;
|
||||
// Try to avoid invoking the resolve hook.
|
||||
if (target->is<JSFunction>() && !target->as<JSFunction>().hasResolvedLength()) {
|
||||
uint16_t len;
|
||||
if (!target->as<JSFunction>().getLength(cx, &len))
|
||||
return nullptr;
|
||||
return false;
|
||||
length = Max(0.0, double(len) - argslen);
|
||||
} else {
|
||||
// Steps 5-6.
|
||||
RootedId id(cx, NameToId(cx->names().length));
|
||||
bool hasLength;
|
||||
if (!HasOwnProperty(cx, target, id, &hasLength))
|
||||
return nullptr;
|
||||
return false;
|
||||
|
||||
// Step 7-8.
|
||||
if (hasLength) {
|
||||
// a-b.
|
||||
RootedValue targetLen(cx);
|
||||
if (!GetProperty(cx, target, target, id, &targetLen))
|
||||
return nullptr;
|
||||
return false;
|
||||
// d.
|
||||
if (targetLen.isNumber())
|
||||
length = Max(0.0, JS::ToInteger(targetLen.toNumber()) - argslen);
|
||||
@@ -1725,7 +1712,7 @@ js::fun_bind(JSContext* cx, HandleObject target, HandleValue thisArg,
|
||||
// Steps 11-12.
|
||||
RootedValue targetName(cx);
|
||||
if (!GetProperty(cx, target, target, cx->names().name, &targetName))
|
||||
return nullptr;
|
||||
return false;
|
||||
|
||||
// Step 13.
|
||||
if (targetName.isString())
|
||||
@@ -1736,23 +1723,23 @@ js::fun_bind(JSContext* cx, HandleObject target, HandleValue thisArg,
|
||||
StringBuffer sb(cx);
|
||||
// Disabled for B2G failures.
|
||||
// if (!sb.append("bound ") || !sb.append(name))
|
||||
// return nullptr;
|
||||
// return false;
|
||||
if (!sb.append(name))
|
||||
return nullptr;
|
||||
return false;
|
||||
|
||||
RootedAtom nameAtom(cx, sb.finishAtom());
|
||||
if (!nameAtom)
|
||||
return nullptr;
|
||||
return false;
|
||||
|
||||
// Step 4.
|
||||
// Step 4.
|
||||
RootedFunction fun(cx, target->isConstructor() ?
|
||||
NewNativeConstructor(cx, CallOrConstructBoundFunction, length, nameAtom) :
|
||||
NewNativeFunction(cx, CallOrConstructBoundFunction, length, nameAtom));
|
||||
if (!fun)
|
||||
return nullptr;
|
||||
return false;
|
||||
|
||||
if (!fun->initBoundFunction(cx, target, thisArg, boundArgs, argslen))
|
||||
return nullptr;
|
||||
return false;
|
||||
|
||||
// Steps 9-10. Set length again, because NewNativeFunction/NewNativeConstructor
|
||||
// sometimes truncates.
|
||||
@@ -1761,11 +1748,13 @@ js::fun_bind(JSContext* cx, HandleObject target, HandleValue thisArg,
|
||||
if (!DefineProperty(cx, fun, cx->names().length, lengthVal, nullptr, nullptr,
|
||||
JSPROP_READONLY))
|
||||
{
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return fun;
|
||||
// Step 15.
|
||||
args.rval().setObject(*fun);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -759,10 +759,6 @@ fun_apply(JSContext* cx, unsigned argc, Value* vp);
|
||||
extern bool
|
||||
fun_call(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
extern JSObject*
|
||||
fun_bind(JSContext* cx, HandleObject target, HandleValue thisArg,
|
||||
Value* boundArgs, unsigned argslen);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
+1
-1
@@ -2937,7 +2937,7 @@ JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHan
|
||||
/* Avoid recursive death when decompiling in ReportValueError. */
|
||||
RootedString str(cx);
|
||||
if (hint == JSTYPE_STRING) {
|
||||
str = JS_InternString(cx, clasp->name);
|
||||
str = JS_AtomizeAndPinString(cx, clasp->name);
|
||||
if (!str)
|
||||
return false;
|
||||
} else {
|
||||
|
||||
+5
-3
@@ -2249,7 +2249,7 @@ Intern(JSContext* cx, unsigned argc, jsval* vp)
|
||||
|
||||
mozilla::Range<const char16_t> chars = strChars.twoByteRange();
|
||||
|
||||
if (!JS_InternUCStringN(cx, chars.start().get(), chars.length()))
|
||||
if (!JS_AtomizeAndPinUCStringN(cx, chars.start().get(), chars.length()))
|
||||
return false;
|
||||
|
||||
args.rval().setUndefined();
|
||||
@@ -4129,7 +4129,8 @@ class ShellAutoEntryMonitor : JS::dbg::AutoEntryMonitor {
|
||||
MOZ_ASSERT(!enteredWithoutExit);
|
||||
}
|
||||
|
||||
void Entry(JSContext* cx, JSFunction* function) override {
|
||||
void Entry(JSContext* cx, JSFunction* function, JS::HandleValue asyncStack,
|
||||
JS::HandleString asyncCause) override {
|
||||
MOZ_ASSERT(!enteredWithoutExit);
|
||||
enteredWithoutExit = true;
|
||||
|
||||
@@ -4143,7 +4144,8 @@ class ShellAutoEntryMonitor : JS::dbg::AutoEntryMonitor {
|
||||
oom = !log.append(make_string_copy("anonymous"));
|
||||
}
|
||||
|
||||
void Entry(JSContext* cx, JSScript* script) override {
|
||||
void Entry(JSContext* cx, JSScript* script, JS::HandleValue asyncStack,
|
||||
JS::HandleString asyncCause) override {
|
||||
MOZ_ASSERT(!enteredWithoutExit);
|
||||
enteredWithoutExit = true;
|
||||
|
||||
|
||||
@@ -7553,7 +7553,7 @@ DebuggerEnv_getType(JSContext* cx, unsigned argc, Value* vp)
|
||||
else
|
||||
s = "object";
|
||||
|
||||
JSAtom* str = Atomize(cx, s, strlen(s), InternAtom);
|
||||
JSAtom* str = Atomize(cx, s, strlen(s), PinAtom);
|
||||
if (!str)
|
||||
return false;
|
||||
args.rval().setString(str);
|
||||
|
||||
@@ -807,6 +807,21 @@ SavedStacks::saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame, unsi
|
||||
return insertFrames(cx, iter, frame, maxFrameCount);
|
||||
}
|
||||
|
||||
bool
|
||||
SavedStacks::copyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString asyncCause,
|
||||
MutableHandleSavedFrame adoptedStack, unsigned maxFrameCount)
|
||||
{
|
||||
MOZ_ASSERT(initialized());
|
||||
assertSameCompartment(cx, this);
|
||||
|
||||
RootedObject asyncStackObj(cx, CheckedUnwrap(asyncStack));
|
||||
MOZ_ASSERT(asyncStackObj);
|
||||
MOZ_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*asyncStackObj));
|
||||
RootedSavedFrame frame(cx, &asyncStackObj->as<js::SavedFrame>());
|
||||
|
||||
return adoptAsyncStack(cx, frame, asyncCause, adoptedStack, maxFrameCount);
|
||||
}
|
||||
|
||||
void
|
||||
SavedStacks::sweep(JSRuntime* rt)
|
||||
{
|
||||
|
||||
@@ -274,6 +274,8 @@ class SavedStacks {
|
||||
bool init();
|
||||
bool initialized() const { return frames.initialized(); }
|
||||
bool saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame, unsigned maxFrameCount = 0);
|
||||
bool copyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString asyncCause,
|
||||
MutableHandleSavedFrame adoptedStack, unsigned maxFrameCount = 0);
|
||||
void sweep(JSRuntime* rt);
|
||||
void trace(JSTracer* trc);
|
||||
uint32_t count();
|
||||
|
||||
@@ -923,10 +923,14 @@ InterpreterActivation::InterpreterActivation(RunState& state, JSContext* cx,
|
||||
MOZ_ASSERT_IF(entryFrame_->isEvalFrame(), state.script()->isActiveEval());
|
||||
|
||||
if (entryMonitor_) {
|
||||
RootedValue stack(cx_);
|
||||
stack.setObjectOrNull(asyncStack_.get());
|
||||
if (!cx_->compartment()->wrap(cx_, &stack))
|
||||
stack.setUndefined();
|
||||
if (entryFrame->isFunctionFrame())
|
||||
entryMonitor_->Entry(cx_, entryFrame->fun());
|
||||
entryMonitor_->Entry(cx_, entryFrame->fun(), stack, asyncCause_);
|
||||
else
|
||||
entryMonitor_->Entry(cx_, entryFrame->script());
|
||||
entryMonitor_->Entry(cx_, entryFrame->script(), stack, asyncCause_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -1404,10 +1404,14 @@ jit::JitActivation::JitActivation(JSContext* cx, CalleeToken entryPoint, bool ac
|
||||
if (entryMonitor_) {
|
||||
MOZ_ASSERT(entryPoint);
|
||||
|
||||
RootedValue stack(cx_);
|
||||
stack.setObjectOrNull(asyncStack_.get());
|
||||
if (!cx_->compartment()->wrap(cx_, &stack))
|
||||
stack.setUndefined();
|
||||
if (CalleeTokenIsFunction(entryPoint))
|
||||
entryMonitor_->Entry(cx_, CalleeTokenToFunction(entryPoint));
|
||||
entryMonitor_->Entry(cx_, CalleeTokenToFunction(entryPoint), stack, asyncCause_);
|
||||
else
|
||||
entryMonitor_->Entry(cx_, CalleeTokenToScript(entryPoint));
|
||||
entryMonitor_->Entry(cx_, CalleeTokenToScript(entryPoint), stack, asyncCause_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* property, it should return a replacement property descriptor for it. If not,
|
||||
* it should return null.
|
||||
*/
|
||||
[scriptable,uuid(215353cb-6e77-462f-a791-6891f42e593f)]
|
||||
[scriptable,uuid(d05cc5fd-ad88-41a6-854c-36fd94d69ddb)]
|
||||
interface nsIAddonInterposition : nsISupports
|
||||
{
|
||||
/**
|
||||
@@ -54,4 +54,14 @@ interface nsIAddonInterposition : nsISupports
|
||||
in jsval originalFunc,
|
||||
in jsval originalThis,
|
||||
in jsval args);
|
||||
|
||||
/**
|
||||
* For the first time when the interposition is registered the engine
|
||||
* calls getWhitelist and expects an array of strings. The strings are
|
||||
* the name of properties the interposition wants interposeProperty
|
||||
* to be called. It can be an empty array.
|
||||
* Note: for CPOWs interposeProperty is always called regardless if
|
||||
* the name of the property is on the whitelist or not.
|
||||
*/
|
||||
jsval getWhitelist();
|
||||
};
|
||||
|
||||
@@ -432,7 +432,7 @@ ExportFunction(JSContext* cx, HandleValue vfunction, HandleValue vscope, HandleV
|
||||
JSFunction* fun = JS_GetObjectFunction(funObj);
|
||||
RootedString funName(cx, JS_GetFunctionId(fun));
|
||||
if (!funName)
|
||||
funName = JS_InternString(cx, "");
|
||||
funName = JS_AtomizeAndPinString(cx, "");
|
||||
|
||||
if (!JS_StringToId(cx, funName, &id))
|
||||
return false;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "nsWindowMemoryReporter.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "ShimInterfaceInfo.h"
|
||||
#include "nsIAddonInterposition.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace JS;
|
||||
@@ -3430,8 +3431,9 @@ nsXPCComponents_Utils::SetAddonInterposition(const nsACString& addonIdStr,
|
||||
JSAddonId* addonId = xpc::NewAddonId(cx, addonIdStr);
|
||||
if (!addonId)
|
||||
return NS_ERROR_FAILURE;
|
||||
if (!XPCWrappedNativeScope::SetAddonInterposition(addonId, interposition))
|
||||
if (!XPCWrappedNativeScope::SetAddonInterposition(cx, addonId, interposition))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -3411,7 +3411,7 @@ XPCJSRuntime::OnJSContextNew(JSContext* cx)
|
||||
if (JSID_IS_VOID(mStrIDs[0])) {
|
||||
RootedString str(cx);
|
||||
for (unsigned i = 0; i < IDX_TOTAL_COUNT; i++) {
|
||||
str = JS_InternString(cx, mStrings[i]);
|
||||
str = JS_AtomizeAndPinString(cx, mStrings[i]);
|
||||
if (!str) {
|
||||
mStrIDs[0] = JSID_VOID;
|
||||
return false;
|
||||
@@ -3445,7 +3445,7 @@ XPCJSRuntime::DescribeCustomObjects(JSObject* obj, const js::Class* clasp,
|
||||
XPCWrappedNativeProto* p =
|
||||
static_cast<XPCWrappedNativeProto*>(xpc_GetJSPrivate(obj));
|
||||
si = p->GetScriptableInfo();
|
||||
|
||||
|
||||
if (!si) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ XPCNativeInterface::NewInstance(nsIInterfaceInfo* aInfo)
|
||||
if (!XPCConvert::IsMethodReflectable(*info))
|
||||
continue;
|
||||
|
||||
str = JS_InternString(cx, info->GetName());
|
||||
str = JS_AtomizeAndPinString(cx, info->GetName());
|
||||
if (!str) {
|
||||
NS_ERROR("bad method name");
|
||||
failed = true;
|
||||
@@ -338,7 +338,7 @@ XPCNativeInterface::NewInstance(nsIInterfaceInfo* aInfo)
|
||||
break;
|
||||
}
|
||||
|
||||
str = JS_InternString(cx, namestr);
|
||||
str = JS_AtomizeAndPinString(cx, namestr);
|
||||
if (!str) {
|
||||
NS_ERROR("bad constant name");
|
||||
failed = true;
|
||||
@@ -364,7 +364,7 @@ XPCNativeInterface::NewInstance(nsIInterfaceInfo* aInfo)
|
||||
if (!failed) {
|
||||
const char* bytes;
|
||||
if (NS_FAILED(aInfo->GetNameShared(&bytes)) || !bytes ||
|
||||
nullptr == (str = JS_InternString(cx, bytes))) {
|
||||
nullptr == (str = JS_AtomizeAndPinString(cx, bytes))) {
|
||||
failed = true;
|
||||
}
|
||||
interfaceName = INTERNED_STRING_TO_JSID(cx, str);
|
||||
|
||||
@@ -27,6 +27,7 @@ using namespace JS;
|
||||
XPCWrappedNativeScope* XPCWrappedNativeScope::gScopes = nullptr;
|
||||
XPCWrappedNativeScope* XPCWrappedNativeScope::gDyingScopes = nullptr;
|
||||
XPCWrappedNativeScope::InterpositionMap* XPCWrappedNativeScope::gInterpositionMap = nullptr;
|
||||
InterpositionWhitelistArray* XPCWrappedNativeScope::gInterpositionWhitelists = nullptr;
|
||||
|
||||
NS_IMPL_ISUPPORTS(XPCWrappedNativeScope::ClearInterpositionsObserver, nsIObserver)
|
||||
|
||||
@@ -46,6 +47,11 @@ XPCWrappedNativeScope::ClearInterpositionsObserver::Observe(nsISupports* subject
|
||||
gInterpositionMap = nullptr;
|
||||
}
|
||||
|
||||
if (gInterpositionWhitelists) {
|
||||
delete gInterpositionWhitelists;
|
||||
gInterpositionWhitelists = nullptr;
|
||||
}
|
||||
|
||||
nsContentUtils::UnregisterShutdownObserver(this);
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -143,6 +149,8 @@ XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext* cx,
|
||||
"extensions.interposition.enabled", false);
|
||||
if (interpositionEnabled) {
|
||||
mInterposition = do_GetService("@mozilla.org/addons/default-addon-shims;1");
|
||||
MOZ_ASSERT(mInterposition);
|
||||
UpdateInterpositionWhitelist(cx, mInterposition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -693,7 +701,8 @@ XPCWrappedNativeScope::SetExpandoChain(JSContext* cx, HandleObject target,
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
XPCWrappedNativeScope::SetAddonInterposition(JSAddonId* addonId,
|
||||
XPCWrappedNativeScope::SetAddonInterposition(JSContext* cx,
|
||||
JSAddonId* addonId,
|
||||
nsIAddonInterposition* interp)
|
||||
{
|
||||
if (!gInterpositionMap) {
|
||||
@@ -701,14 +710,17 @@ XPCWrappedNativeScope::SetAddonInterposition(JSAddonId* addonId,
|
||||
gInterpositionMap->init();
|
||||
|
||||
// Make sure to clear the map at shutdown.
|
||||
// Note: this will take care of gInterpositionWhitelists too.
|
||||
nsContentUtils::RegisterShutdownObserver(new ClearInterpositionsObserver());
|
||||
}
|
||||
if (interp) {
|
||||
return gInterpositionMap->put(addonId, interp);
|
||||
bool ok = gInterpositionMap->put(addonId, interp);
|
||||
NS_ENSURE_TRUE(ok, false);
|
||||
UpdateInterpositionWhitelist(cx, interp);
|
||||
} else {
|
||||
gInterpositionMap->remove(addonId);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAddonInterposition>
|
||||
@@ -717,6 +729,103 @@ XPCWrappedNativeScope::GetInterposition()
|
||||
return mInterposition;
|
||||
}
|
||||
|
||||
/* static */ InterpositionWhitelist*
|
||||
XPCWrappedNativeScope::GetInterpositionWhitelist(nsIAddonInterposition* interposition)
|
||||
{
|
||||
if (!gInterpositionWhitelists)
|
||||
return nullptr;
|
||||
|
||||
InterpositionWhitelistArray& wls = *gInterpositionWhitelists;
|
||||
for (size_t i = 0; i < wls.Length(); i++) {
|
||||
if (wls[i].interposition == interposition)
|
||||
return &wls[i].whitelist;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
XPCWrappedNativeScope::UpdateInterpositionWhitelist(JSContext* cx,
|
||||
nsIAddonInterposition* interposition)
|
||||
{
|
||||
// We want to set the interpostion whitelist only once.
|
||||
InterpositionWhitelist* whitelist = GetInterpositionWhitelist(interposition);
|
||||
if (whitelist)
|
||||
return true;
|
||||
|
||||
// The hashsets in gInterpositionWhitelists do not have a copy constructor so
|
||||
// a reallocation for the array will lead to a memory corruption. If you
|
||||
// need more interpositions, change the capacity of the array please.
|
||||
static const size_t MAX_INTERPOSITION = 8;
|
||||
if (!gInterpositionWhitelists)
|
||||
gInterpositionWhitelists = new InterpositionWhitelistArray(MAX_INTERPOSITION);
|
||||
|
||||
MOZ_RELEASE_ASSERT(MAX_INTERPOSITION > gInterpositionWhitelists->Length() + 1);
|
||||
InterpositionWhitelistPair* newPair = gInterpositionWhitelists->AppendElement();
|
||||
newPair->interposition = interposition;
|
||||
newPair->whitelist.init();
|
||||
whitelist = &newPair->whitelist;
|
||||
|
||||
RootedValue whitelistVal(cx);
|
||||
nsresult rv = interposition->GetWhitelist(&whitelistVal);
|
||||
if (NS_FAILED(rv)) {
|
||||
JS_ReportError(cx, "Could not get the whitelist from the interposition.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!whitelistVal.isObject()) {
|
||||
JS_ReportError(cx, "Whitelist must be an array.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// We want to enter the whitelist's compartment to avoid any wrappers.
|
||||
// To be on the safe side let's make sure that it's a system compartment
|
||||
// and we don't accidentally trigger some content function here by parsing
|
||||
// the whitelist object.
|
||||
RootedObject whitelistObj(cx, &whitelistVal.toObject());
|
||||
whitelistObj = js::UncheckedUnwrap(whitelistObj);
|
||||
if (!AccessCheck::isChrome(whitelistObj)) {
|
||||
JS_ReportError(cx, "Whitelist must be from system scope.");
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
JSAutoCompartment ac(cx, whitelistObj);
|
||||
|
||||
uint32_t length;
|
||||
if (!JS_IsArrayObject(cx, whitelistObj) ||
|
||||
!JS_GetArrayLength(cx, whitelistObj, &length)) {
|
||||
JS_ReportError(cx, "Whitelist must be an array.");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
RootedValue idval(cx);
|
||||
if (!JS_GetElement(cx, whitelistObj, i, &idval))
|
||||
return false;
|
||||
|
||||
if (!idval.isString()) {
|
||||
JS_ReportError(cx, "Whitelist must contain strings only.");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedString str(cx, idval.toString());
|
||||
str = JS_AtomizeAndPinJSString(cx, str);
|
||||
if (!str) {
|
||||
JS_ReportError(cx, "String internization failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// By internizing the id's we ensure that they won't get
|
||||
// GCed so we can use them as hash keys.
|
||||
jsid id = INTERNED_STRING_TO_JSID(cx, str);
|
||||
whitelist->put(JSID_BITS(id));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
// static
|
||||
|
||||
@@ -1347,17 +1347,14 @@ bool
|
||||
SetAddonInterposition(const nsACString& addonIdStr, nsIAddonInterposition* interposition)
|
||||
{
|
||||
JSAddonId* addonId;
|
||||
{
|
||||
// We enter the junk scope just to allocate a string, which actually will go
|
||||
// in the system zone.
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init(xpc::PrivilegedJunkScope());
|
||||
addonId = NewAddonId(jsapi.cx(), addonIdStr);
|
||||
if (!addonId)
|
||||
return false;
|
||||
}
|
||||
|
||||
return XPCWrappedNativeScope::SetAddonInterposition(addonId, interposition);
|
||||
// We enter the junk scope just to allocate a string, which actually will go
|
||||
// in the system zone.
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init(xpc::PrivilegedJunkScope());
|
||||
addonId = NewAddonId(jsapi.cx(), addonIdStr);
|
||||
if (!addonId)
|
||||
return false;
|
||||
return XPCWrappedNativeScope::SetAddonInterposition(jsapi.cx(), addonId, interposition);
|
||||
}
|
||||
|
||||
} // namespace xpc
|
||||
|
||||
@@ -1014,6 +1014,17 @@ static inline bool IS_PROTO_CLASS(const js::Class* clazz)
|
||||
clazz == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass;
|
||||
}
|
||||
|
||||
typedef js::HashSet<size_t,
|
||||
js::DefaultHasher<size_t>,
|
||||
js::SystemAllocPolicy> InterpositionWhitelist;
|
||||
|
||||
struct InterpositionWhitelistPair {
|
||||
nsIAddonInterposition* interposition;
|
||||
InterpositionWhitelist whitelist;
|
||||
};
|
||||
|
||||
typedef nsTArray<InterpositionWhitelistPair> InterpositionWhitelistArray;
|
||||
|
||||
/***************************************************************************/
|
||||
// XPCWrappedNativeScope is one-to-one with a JS global object.
|
||||
|
||||
@@ -1184,9 +1195,14 @@ public:
|
||||
bool HasInterposition() { return mInterposition; }
|
||||
nsCOMPtr<nsIAddonInterposition> GetInterposition();
|
||||
|
||||
static bool SetAddonInterposition(JSAddonId* addonId,
|
||||
static bool SetAddonInterposition(JSContext* cx,
|
||||
JSAddonId* addonId,
|
||||
nsIAddonInterposition* interp);
|
||||
|
||||
static InterpositionWhitelist* GetInterpositionWhitelist(nsIAddonInterposition* interposition);
|
||||
static bool UpdateInterpositionWhitelist(JSContext* cx,
|
||||
nsIAddonInterposition* interposition);
|
||||
|
||||
void SetAddonCallInterposition() { mHasCallInterpositions = true; }
|
||||
bool HasCallInterposition() { return mHasCallInterpositions; };
|
||||
|
||||
@@ -1209,6 +1225,8 @@ private:
|
||||
|
||||
static InterpositionMap* gInterpositionMap;
|
||||
|
||||
static InterpositionWhitelistArray* gInterpositionWhitelists;
|
||||
|
||||
XPCJSRuntime* mRuntime;
|
||||
Native2WrappedNativeMap* mWrappedNativeMap;
|
||||
ClassInfo2WrappedNativeProtoMap* mWrappedNativeProtoMap;
|
||||
|
||||
@@ -34,6 +34,11 @@ let TestInterposition = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonInterposition,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
getWhitelist: function() {
|
||||
return ["abcxyz", "utils", "dataprop", "getterprop", "setterprop",
|
||||
"objprop", "defineprop", "configurableprop"];
|
||||
},
|
||||
|
||||
interposeProperty: function(addonId, target, iid, prop) {
|
||||
do_check_eq(addonId, ADDONID);
|
||||
do_check_eq(gExpectedProp, prop);
|
||||
|
||||
@@ -29,10 +29,12 @@ InterposeProperty(JSContext* cx, HandleObject target, const nsIID* iid, HandleId
|
||||
// wrapped natives.
|
||||
RootedObject unwrapped(cx, UncheckedUnwrap(target));
|
||||
const js::Class* clasp = js::GetObjectClass(unwrapped);
|
||||
bool isCPOW = jsipc::IsWrappedCPOW(unwrapped);
|
||||
if (!mozilla::dom::IsDOMClass(clasp) &&
|
||||
!IS_WN_CLASS(clasp) &&
|
||||
!IS_PROTO_CLASS(clasp) &&
|
||||
clasp != &OuterWindowProxyClass) {
|
||||
clasp != &OuterWindowProxyClass &&
|
||||
!isCPOW) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -40,6 +42,12 @@ InterposeProperty(JSContext* cx, HandleObject target, const nsIID* iid, HandleId
|
||||
MOZ_ASSERT(scope->HasInterposition());
|
||||
|
||||
nsCOMPtr<nsIAddonInterposition> interp = scope->GetInterposition();
|
||||
InterpositionWhitelist* wl = XPCWrappedNativeScope::GetInterpositionWhitelist(interp);
|
||||
// We do InterposeProperty only if the id is on the whitelist of the interpostion
|
||||
// or if the target is a CPOW.
|
||||
if ((!wl || !wl->has(JSID_BITS(id.get()))) && !isCPOW)
|
||||
return true;
|
||||
|
||||
JSAddonId* addonId = AddonIdOfObject(target);
|
||||
RootedValue addonIdValue(cx, StringValue(StringOfAddonId(addonId)));
|
||||
RootedValue prop(cx, IdToValue(id));
|
||||
|
||||
@@ -27,7 +27,8 @@ public:
|
||||
virtual void Prev() override;
|
||||
|
||||
nsFrameIterator(nsPresContext* aPresContext, nsIFrame *aStart,
|
||||
nsIteratorType aType, bool aLockScroll, bool aFollowOOFs);
|
||||
nsIteratorType aType, bool aLockScroll, bool aFollowOOFs,
|
||||
bool aSkipPopupChecks);
|
||||
|
||||
protected:
|
||||
virtual ~nsFrameIterator() {}
|
||||
@@ -87,6 +88,7 @@ protected:
|
||||
nsPresContext* const mPresContext;
|
||||
const bool mLockScroll;
|
||||
const bool mFollowOOFs;
|
||||
const bool mSkipPopupChecks;
|
||||
const nsIteratorType mType;
|
||||
|
||||
private:
|
||||
@@ -103,8 +105,9 @@ class nsVisualIterator: public nsFrameIterator
|
||||
{
|
||||
public:
|
||||
nsVisualIterator(nsPresContext* aPresContext, nsIFrame *aStart,
|
||||
nsIteratorType aType, bool aLockScroll, bool aFollowOOFs) :
|
||||
nsFrameIterator(aPresContext, aStart, aType, aLockScroll, aFollowOOFs) {}
|
||||
nsIteratorType aType, bool aLockScroll,
|
||||
bool aFollowOOFs, bool aSkipPopupChecks) :
|
||||
nsFrameIterator(aPresContext, aStart, aType, aLockScroll, aFollowOOFs, aSkipPopupChecks) {}
|
||||
|
||||
protected:
|
||||
nsIFrame* GetFirstChildInner(nsIFrame* aFrame) override;
|
||||
@@ -136,7 +139,8 @@ NS_NewFrameTraversal(nsIFrameEnumerator **aEnumerator,
|
||||
nsIteratorType aType,
|
||||
bool aVisual,
|
||||
bool aLockInScrollView,
|
||||
bool aFollowOOFs)
|
||||
bool aFollowOOFs,
|
||||
bool aSkipPopupChecks)
|
||||
{
|
||||
if (!aEnumerator || !aStart)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
@@ -148,10 +152,10 @@ NS_NewFrameTraversal(nsIFrameEnumerator **aEnumerator,
|
||||
nsCOMPtr<nsIFrameEnumerator> trav;
|
||||
if (aVisual) {
|
||||
trav = new nsVisualIterator(aPresContext, aStart, aType,
|
||||
aLockInScrollView, aFollowOOFs);
|
||||
aLockInScrollView, aFollowOOFs, aSkipPopupChecks);
|
||||
} else {
|
||||
trav = new nsFrameIterator(aPresContext, aStart, aType,
|
||||
aLockInScrollView, aFollowOOFs);
|
||||
aLockInScrollView, aFollowOOFs, aSkipPopupChecks);
|
||||
}
|
||||
trav.forget(aEnumerator);
|
||||
return NS_OK;
|
||||
@@ -175,11 +179,12 @@ NS_IMETHODIMP
|
||||
int32_t aType,
|
||||
bool aVisual,
|
||||
bool aLockInScrollView,
|
||||
bool aFollowOOFs)
|
||||
bool aFollowOOFs,
|
||||
bool aSkipPopupChecks)
|
||||
{
|
||||
return NS_NewFrameTraversal(aEnumerator, aPresContext, aStart,
|
||||
static_cast<nsIteratorType>(aType),
|
||||
aVisual, aLockInScrollView, aFollowOOFs);
|
||||
aVisual, aLockInScrollView, aFollowOOFs, aSkipPopupChecks);
|
||||
}
|
||||
|
||||
// nsFrameIterator implementation
|
||||
@@ -188,10 +193,11 @@ NS_IMPL_ISUPPORTS(nsFrameIterator, nsIFrameEnumerator)
|
||||
|
||||
nsFrameIterator::nsFrameIterator(nsPresContext* aPresContext, nsIFrame *aStart,
|
||||
nsIteratorType aType, bool aLockInScrollView,
|
||||
bool aFollowOOFs)
|
||||
bool aFollowOOFs, bool aSkipPopupChecks)
|
||||
: mPresContext(aPresContext),
|
||||
mLockScroll(aLockInScrollView),
|
||||
mFollowOOFs(aFollowOOFs),
|
||||
mSkipPopupChecks(aSkipPopupChecks),
|
||||
mType(aType),
|
||||
mStart(aStart),
|
||||
mCurrent(aStart),
|
||||
@@ -242,7 +248,7 @@ nsFrameIterator::Last()
|
||||
nsIFrame* parent = getCurrent();
|
||||
// If the current frame is a popup, don't move farther up the tree.
|
||||
// Otherwise, get the nearest root frame or popup.
|
||||
if (parent->GetType() != nsGkAtoms::menuPopupFrame) {
|
||||
if (mSkipPopupChecks || parent->GetType() != nsGkAtoms::menuPopupFrame) {
|
||||
while (!IsRootFrame(parent) && (result = GetParentFrameNotPopup(parent)))
|
||||
parent = result;
|
||||
}
|
||||
@@ -499,6 +505,11 @@ nsFrameIterator::GetPlaceholderFrame(nsIFrame* aFrame)
|
||||
bool
|
||||
nsFrameIterator::IsPopupFrame(nsIFrame* aFrame)
|
||||
{
|
||||
// If skipping popup checks, pretend this isn't one.
|
||||
if (mSkipPopupChecks) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (aFrame &&
|
||||
aFrame->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_POPUP);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ nsresult NS_NewFrameTraversal(nsIFrameEnumerator **aEnumerator,
|
||||
nsIteratorType aType,
|
||||
bool aVisual,
|
||||
bool aLockInScrollView,
|
||||
bool aFollowOOFs);
|
||||
bool aFollowOOFs,
|
||||
bool aSkipPopupChecks);
|
||||
|
||||
nsresult NS_CreateFrameTraversal(nsIFrameTraversal** aResult);
|
||||
|
||||
@@ -33,7 +34,8 @@ public:
|
||||
int32_t aType,
|
||||
bool aVisual,
|
||||
bool aLockInScrollView,
|
||||
bool aFollowOOFs) override;
|
||||
bool aFollowOOFs,
|
||||
bool aSkipPopupChecks) override;
|
||||
|
||||
protected:
|
||||
virtual ~nsFrameTraversal();
|
||||
|
||||
@@ -34,9 +34,9 @@ enum nsIteratorType {
|
||||
ePostOrder
|
||||
};
|
||||
|
||||
// {9d469828-9bf2-4151-a385-05f30219221b}
|
||||
// {d33fe76c-207c-4359-a315-8eb1eecf80e5}
|
||||
#define NS_IFRAMETRAVERSAL_IID \
|
||||
{ 0x9d469828, 0x9bf2, 0x4151, { 0xa3, 0x85, 0x05, 0xf3, 0x02, 0x19, 0x22, 0x1b } }
|
||||
{ 0xd33fe76c, 0x207c, 0x4359, { 0xa3, 0x15, 0x8e, 0xb1, 0xee, 0xcf, 0x80, 0xe5 } }
|
||||
|
||||
class nsIFrameTraversal : public nsISupports
|
||||
{
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
* If true, when reaching a placeholder frame while going down will get
|
||||
* the real frame. Going back up will go on past the placeholder,
|
||||
* so the placeholders are logically part of the frame tree.
|
||||
* @param aSkipPopupChecks [in] if false, then don't iterate into or out of a
|
||||
* popup frame. If true, skip any popup related checks.
|
||||
*/
|
||||
NS_IMETHOD NewFrameTraversal(nsIFrameEnumerator **aEnumerator,
|
||||
nsPresContext* aPresContext,
|
||||
@@ -64,7 +66,8 @@ public:
|
||||
int32_t aType,
|
||||
bool aVisual,
|
||||
bool aLockInScrollView,
|
||||
bool aFollowOOFs) = 0;
|
||||
bool aFollowOOFs,
|
||||
bool aSkipPopupChecks) = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIFrameTraversal, NS_IFRAMETRAVERSAL_IID)
|
||||
|
||||
@@ -6232,7 +6232,8 @@ nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
|
||||
ePostOrder,
|
||||
false, // aVisual
|
||||
aPos->mScrollViewStop,
|
||||
false // aFollowOOFs
|
||||
false, // aFollowOOFs
|
||||
false // aSkipPopupChecks
|
||||
);
|
||||
if (NS_FAILED(result))
|
||||
return result;
|
||||
@@ -6332,7 +6333,8 @@ nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
|
||||
eLeaf,
|
||||
false, // aVisual
|
||||
aPos->mScrollViewStop,
|
||||
false // aFollowOOFs
|
||||
false, // aFollowOOFs
|
||||
false // aSkipPopupChecks
|
||||
);
|
||||
}
|
||||
while ( !found ){
|
||||
@@ -7114,7 +7116,8 @@ nsIFrame::GetFrameFromDirection(nsDirection aDirection, bool aVisual,
|
||||
eLeaf,
|
||||
aVisual && presContext->BidiEnabled(),
|
||||
aScrollViewStop,
|
||||
true // aFollowOOFs
|
||||
true, // aFollowOOFs
|
||||
false // aSkipPopupChecks
|
||||
);
|
||||
if (NS_FAILED(result))
|
||||
return result;
|
||||
|
||||
@@ -1357,7 +1357,8 @@ nsFrameSelection::GetFrameFromLevel(nsIFrame *aFrameIn,
|
||||
eLeaf,
|
||||
false, // aVisual
|
||||
false, // aLockInScrollView
|
||||
false // aFollowOOFs
|
||||
false, // aFollowOOFs
|
||||
false // aSkipPopupChecks
|
||||
);
|
||||
if (NS_FAILED(result))
|
||||
return result;
|
||||
|
||||
@@ -22,6 +22,10 @@ DefaultInterpositionService.prototype = {
|
||||
classID: Components.ID("{50bc93ce-602a-4bef-bf3a-61fc749c4caf}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonInterposition, Ci.nsISupportsWeakReference]),
|
||||
|
||||
getWhitelist: function() {
|
||||
return [];
|
||||
},
|
||||
|
||||
interposeProperty: function(addon, target, iid, prop) {
|
||||
return null;
|
||||
},
|
||||
|
||||
@@ -8,6 +8,7 @@ TEST_DIRS += ['tests']
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'addoncompat.manifest',
|
||||
'defaultShims.js',
|
||||
'multiprocessShims.js',
|
||||
'remoteTagService.js',
|
||||
]
|
||||
|
||||
@@ -70,12 +70,42 @@ function AddonInterpositionService()
|
||||
// kinds of objects.
|
||||
this._interfaceInterpositions = RemoteAddonsParent.getInterfaceInterpositions();
|
||||
this._taggedInterpositions = RemoteAddonsParent.getTaggedInterpositions();
|
||||
|
||||
let wl = [];
|
||||
for (let v in this._interfaceInterpositions) {
|
||||
let interp = this._interfaceInterpositions[v];
|
||||
wl.push(...Object.getOwnPropertyNames(interp.methods));
|
||||
wl.push(...Object.getOwnPropertyNames(interp.getters));
|
||||
wl.push(...Object.getOwnPropertyNames(interp.setters));
|
||||
}
|
||||
|
||||
for (let v in this._taggedInterpositions) {
|
||||
let interp = this._taggedInterpositions[v];
|
||||
wl.push(...Object.getOwnPropertyNames(interp.methods));
|
||||
wl.push(...Object.getOwnPropertyNames(interp.getters));
|
||||
wl.push(...Object.getOwnPropertyNames(interp.setters));
|
||||
}
|
||||
|
||||
let nameSet = new Set();
|
||||
wl = wl.filter(function(item) {
|
||||
if (nameSet.has(item))
|
||||
return true;
|
||||
|
||||
nameSet.add(item);
|
||||
return true;
|
||||
});
|
||||
|
||||
this._whitelist = wl;
|
||||
}
|
||||
|
||||
AddonInterpositionService.prototype = {
|
||||
classID: Components.ID("{1363d5f0-d95e-11e3-9c1a-0800200c9a66}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonInterposition, Ci.nsISupportsWeakReference]),
|
||||
|
||||
getWhitelist: function() {
|
||||
return this._whitelist;
|
||||
},
|
||||
|
||||
// When the interface is not known for a method call, this code
|
||||
// determines the type of the target object.
|
||||
getObjectTag: function(target) {
|
||||
|
||||
@@ -1251,7 +1251,8 @@ nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell,
|
||||
eLeaf,
|
||||
false, // aVisual
|
||||
false, // aLockInScrollView
|
||||
false // aFollowOOFs
|
||||
false, // aFollowOOFs
|
||||
false // aSkipPopupChecks
|
||||
);
|
||||
|
||||
if (!frameTraversal)
|
||||
|
||||
Reference in New Issue
Block a user