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 1169044 - Patch 1 - Refactor setting referrer and referrer policy between fetch and XHR. r=khuey (3912ebaef)
- Bug 1150771 - Let ArrayBuffer through object Xrays. r=gabor (bed760277)
- Bug 1151385 - Fail early for cross-origin sandboxPrototype. r=gabor (3b65b1561)
- Bug 1131707 - Transparently forward the construct bit for function forwarders. r=gabor (1f5792775)
- Bug 1170311 - Stop asserting non-null argument to nsIPrincipal::{subsumes,equals}{,ConsideringDomain}. r=gabor (7e36d6683)
- Bug 1171175 - Improve BasePrincipal::IsCodebasePrincipal. r=baku (0d278e8f4)
- Bug 1174731 - patch 1 - Make searchParams attribute readonly, r=smaug (11f5d6dcf)
- Bug 1174731 - patch 2 - Make searchParams attribute readonly, r=annevk (4aa7ea1e4)
- Bug 1170097 - Part 1: Move OriginAttributeDictionary. r=bholley (63a1139dd)
- Bug 1084525 - Part 1: Create initial PromisesActor skeleton r=fitzgen (2ef0ad37d)
- Bug 1131643 - Implement a Location object;r=jlong (710fb9b79)
- Bug 1129834 - Store BreakpointActors by original location;r=jlong (67d16d37a)
- Bug 1129837 - Remove generatedLocation from BreakpointActor;r=jlongster (018a60746)
- Bug 1082837 - test cases for image redirects loaded from the imagelib cache. r=smaug, ckerschb (49d216725)
- Bug 1073352, part 2 - Enable some devtools tests. r=ejpbruel (0de7cfdc0)
- Bug 1131646 - Clean up the breakpoint code;r=jlongster (7fa9c6a76)
- Bug 1136146 - Merge the latest version of the source-map library with fx-team;r=fitzgen (983f2c2e9)
- Bug 1042976 follow up - Remove getInnerId from script actors; r=Mossop (43f935298)
- Bug 837630 - Stop hiding __proto__ from O.getOwnPropertyNames. r=Waldo,peterv,past (0f321614d)
- Bug 1138975 - Refactor breakpoint sliding for non-source mapped sources;r=jlong (9fd4be4e4)
- Fix breaking on the "load" event in the debugger (bug 1054159). r=ochameau (6b6b40e78)
- Bug 983469 - Pause on breakpoint condition exception. r=fitzgen (fb6dfab57)
- Bug 1135435 - Add UI for breakpoint condition throws. r=fitzgen (b2f49cb03)
- Bug 1137384 - Rename ThreadSources as TabSources and move it up into the TabActor. r=ejpbruel (fdf1db5d9)
This commit is contained in:
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* Description of the Tests for
|
||||
* - Bug 418354 - Call Mixed content blocking on redirects
|
||||
*
|
||||
* Single redirect script tests
|
||||
* 1. Load a script over https inside an https page
|
||||
* - the server responds with a 302 redirect to a >> HTTP << script
|
||||
* - the doorhanger should appear!
|
||||
*
|
||||
* 2. Load a script over https inside an http page
|
||||
* - the server responds with a 302 redirect to a >> HTTP << script
|
||||
* - the doorhanger should not appear!
|
||||
*
|
||||
* Single redirect image tests
|
||||
* 3. Load an image over https inside an https page
|
||||
* - the server responds with a 302 redirect to a >> HTTP << image
|
||||
* - the image should not load
|
||||
*
|
||||
* 4. Load an image over https inside an http page
|
||||
* - the server responds with a 302 redirect to a >> HTTP << image
|
||||
* - the image should load and get cached
|
||||
*
|
||||
* Single redirect cached image tests
|
||||
* 5. Using offline mode to ensure we hit the cache, load a cached image over
|
||||
* https inside an http page
|
||||
* - the server would have responded with a 302 redirect to a >> HTTP <<
|
||||
* image, but instead we try to use the cached image.
|
||||
* - the image should load
|
||||
*
|
||||
* 6. Using offline mode to ensure we hit the cache, load a cached image over
|
||||
* https inside an https page
|
||||
* - the server would have responded with a 302 redirect to a >> HTTP <<
|
||||
* image, but instead we try to use the cached image.
|
||||
* - the image should not load
|
||||
*
|
||||
* Double redirect image test
|
||||
* 7. Load an image over https inside an http page
|
||||
* - the server responds with a 302 redirect to a >> HTTP << server
|
||||
* - the HTTP server responds with a 302 redirect to a >> HTTPS << image
|
||||
* - the image should load and get cached
|
||||
*
|
||||
* Double redirect cached image tests
|
||||
* 8. Using offline mode to ensure we hit the cache, load a cached image over
|
||||
* https inside an http page
|
||||
* - the image would have gone through two redirects: HTTPS->HTTP->HTTPS,
|
||||
* but instead we try to use the cached image.
|
||||
* - the image should load
|
||||
*
|
||||
* 9. Using offline mode to ensure we hit the cache, load a cached image over
|
||||
* https inside an https page
|
||||
* - the image would have gone through two redirects: HTTPS->HTTP->HTTPS,
|
||||
* but instead we try to use the cached image.
|
||||
* - the image should not load
|
||||
*/
|
||||
|
||||
const PREF_ACTIVE = "security.mixed_content.block_active_content";
|
||||
const PREF_DISPLAY = "security.mixed_content.block_display_content";
|
||||
const gHttpsTestRoot = "https://example.com/browser/browser/base/content/test/general/";
|
||||
const gHttpTestRoot = "http://example.com/browser/browser/base/content/test/general/";
|
||||
|
||||
let origBlockActive;
|
||||
let origBlockDisplay;
|
||||
var gTestBrowser = null;
|
||||
|
||||
//------------------------ Helper Functions ---------------------
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
// Set preferences back to their original values
|
||||
Services.prefs.setBoolPref(PREF_ACTIVE, origBlockActive);
|
||||
Services.prefs.setBoolPref(PREF_DISPLAY, origBlockDisplay);
|
||||
|
||||
// Make sure we are online again
|
||||
Services.io.offline = false;
|
||||
});
|
||||
|
||||
function cleanUpAfterTests() {
|
||||
gBrowser.removeCurrentTab();
|
||||
window.focus();
|
||||
finish();
|
||||
}
|
||||
|
||||
function waitForCondition(condition, nextTest, errorMsg, okMsg) {
|
||||
var tries = 0;
|
||||
var interval = setInterval(function() {
|
||||
if (tries >= 30) {
|
||||
ok(false, errorMsg);
|
||||
moveOn();
|
||||
}
|
||||
if (condition()) {
|
||||
ok(true, okMsg)
|
||||
moveOn();
|
||||
}
|
||||
tries++;
|
||||
}, 100);
|
||||
var moveOn = function() {
|
||||
clearInterval(interval); nextTest();
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------ Test 1 ------------------------------
|
||||
|
||||
function test1() {
|
||||
gTestBrowser.addEventListener("load", checkPopUpNotificationsForTest1, true);
|
||||
var url = gHttpsTestRoot + "test_mcb_redirect.html"
|
||||
gTestBrowser.contentWindow.location = url;
|
||||
}
|
||||
|
||||
function checkPopUpNotificationsForTest1() {
|
||||
gTestBrowser.removeEventListener("load", checkPopUpNotificationsForTest1, true);
|
||||
|
||||
var notification = PopupNotifications.getNotification("bad-content", gTestBrowser.selectedBrowser);
|
||||
ok(notification, "OK: Mixed Content Doorhanger appeared in Test1!");
|
||||
|
||||
var expected = "script blocked";
|
||||
waitForCondition(
|
||||
function() content.document.getElementById('mctestdiv').innerHTML == expected,
|
||||
test2, "Error: Waited too long for status in Test 1!",
|
||||
"OK: Expected result in innerHTML for Test1!");
|
||||
}
|
||||
|
||||
//------------------------ Test 2 ------------------------------
|
||||
|
||||
function test2() {
|
||||
gTestBrowser.addEventListener("load", checkPopUpNotificationsForTest2, true);
|
||||
var url = gHttpTestRoot + "test_mcb_redirect.html"
|
||||
gTestBrowser.contentWindow.location = url;
|
||||
}
|
||||
|
||||
function checkPopUpNotificationsForTest2() {
|
||||
gTestBrowser.removeEventListener("load", checkPopUpNotificationsForTest2, true);
|
||||
|
||||
var notification = PopupNotifications.getNotification("bad-content", gTestBrowser.selectedBrowser);
|
||||
ok(!notification, "OK: Mixed Content Doorhanger did not appear in 2!");
|
||||
|
||||
var expected = "script executed";
|
||||
waitForCondition(
|
||||
function() content.document.getElementById('mctestdiv').innerHTML == expected,
|
||||
test3, "Error: Waited too long for status in Test 2!",
|
||||
"OK: Expected result in innerHTML for Test2!");
|
||||
}
|
||||
|
||||
//------------------------ Test 3 ------------------------------
|
||||
// HTTPS page loading insecure image
|
||||
function test3() {
|
||||
gTestBrowser.addEventListener("load", checkLoadEventForTest3, true);
|
||||
var url = gHttpsTestRoot + "test_mcb_redirect_image.html"
|
||||
gTestBrowser.contentWindow.location = url;
|
||||
}
|
||||
|
||||
function checkLoadEventForTest3() {
|
||||
gTestBrowser.removeEventListener("load", checkLoadEventForTest3, true);
|
||||
|
||||
var expected = "image blocked"
|
||||
waitForCondition(
|
||||
function() content.document.getElementById('mctestdiv').innerHTML == expected,
|
||||
test4, "Error: Waited too long for status in Test 3!",
|
||||
"OK: Expected result in innerHTML for Test3!");
|
||||
}
|
||||
|
||||
//------------------------ Test 4 ------------------------------
|
||||
// HTTP page loading insecure image
|
||||
function test4() {
|
||||
gTestBrowser.addEventListener("load", checkLoadEventForTest4, true);
|
||||
var url = gHttpTestRoot + "test_mcb_redirect_image.html"
|
||||
gTestBrowser.contentWindow.location = url;
|
||||
}
|
||||
|
||||
function checkLoadEventForTest4() {
|
||||
gTestBrowser.removeEventListener("load", checkLoadEventForTest4, true);
|
||||
|
||||
var expected = "image loaded"
|
||||
waitForCondition(
|
||||
function() content.document.getElementById('mctestdiv').innerHTML == expected,
|
||||
test5, "Error: Waited too long for status in Test 4!",
|
||||
"OK: Expected result in innerHTML for Test4!");
|
||||
}
|
||||
|
||||
//------------------------ Test 5 ------------------------------
|
||||
// HTTP page laoding insecure cached image
|
||||
// Assuming test 4 succeeded, the image has already been loaded once
|
||||
// and hence should be cached per the sjs cache-control header
|
||||
// Going into offline mode to ensure we are loading from the cache.
|
||||
function test5() {
|
||||
gTestBrowser.addEventListener("load", checkLoadEventForTest5, true);
|
||||
// Go into offline mode
|
||||
Services.io.offline = true;
|
||||
var url = gHttpTestRoot + "test_mcb_redirect_image.html"
|
||||
gTestBrowser.contentWindow.location = url;
|
||||
}
|
||||
|
||||
function checkLoadEventForTest5() {
|
||||
gTestBrowser.removeEventListener("load", checkLoadEventForTest5, true);
|
||||
|
||||
var expected = "image loaded"
|
||||
waitForCondition(
|
||||
function() content.document.getElementById('mctestdiv').innerHTML == expected,
|
||||
test6, "Error: Waited too long for status in Test 5!",
|
||||
"OK: Expected result in innerHTML for Test5!");
|
||||
// Go back online
|
||||
Services.io.offline = false;
|
||||
}
|
||||
|
||||
//------------------------ Test 6 ------------------------------
|
||||
// HTTPS page loading insecure cached image
|
||||
// Assuming test 4 succeeded, the image has already been loaded once
|
||||
// and hence should be cached per the sjs cache-control header
|
||||
// Going into offline mode to ensure we are loading from the cache.
|
||||
function test6() {
|
||||
gTestBrowser.addEventListener("load", checkLoadEventForTest6, true);
|
||||
// Go into offline mode
|
||||
Services.io.offline = true;
|
||||
var url = gHttpsTestRoot + "test_mcb_redirect_image.html"
|
||||
gTestBrowser.contentWindow.location = url;
|
||||
}
|
||||
|
||||
function checkLoadEventForTest6() {
|
||||
gTestBrowser.removeEventListener("load", checkLoadEventForTest6, true);
|
||||
|
||||
var expected = "image blocked"
|
||||
waitForCondition(
|
||||
function() content.document.getElementById('mctestdiv').innerHTML == expected,
|
||||
test7, "Error: Waited too long for status in Test 6!",
|
||||
"OK: Expected result in innerHTML for Test6!");
|
||||
// Go back online
|
||||
Services.io.offline = false;
|
||||
}
|
||||
|
||||
//------------------------ Test 7 ------------------------------
|
||||
// HTTP page loading insecure image that went through a double redirect
|
||||
function test7() {
|
||||
gTestBrowser.addEventListener("load", checkLoadEventForTest7, true);
|
||||
var url = gHttpTestRoot + "test_mcb_double_redirect_image.html"
|
||||
gTestBrowser.contentWindow.location = url;
|
||||
}
|
||||
|
||||
function checkLoadEventForTest7() {
|
||||
gTestBrowser.removeEventListener("load", checkLoadEventForTest7, true);
|
||||
|
||||
var expected = "image loaded"
|
||||
waitForCondition(
|
||||
function() content.document.getElementById('mctestdiv').innerHTML == expected,
|
||||
test8, "Error: Waited too long for status in Test 7!",
|
||||
"OK: Expected result in innerHTML for Test7!");
|
||||
}
|
||||
|
||||
//------------------------ Test 8 ------------------------------
|
||||
// HTTP page loading insecure cached image that went through a double redirect
|
||||
// Assuming test 7 succeeded, the image has already been loaded once
|
||||
// and hence should be cached per the sjs cache-control header
|
||||
// Going into offline mode to ensure we are loading from the cache.
|
||||
function test8() {
|
||||
gTestBrowser.addEventListener("load", checkLoadEventForTest8, true);
|
||||
// Go into offline mode
|
||||
Services.io.offline = true;
|
||||
var url = gHttpTestRoot + "test_mcb_double_redirect_image.html"
|
||||
gTestBrowser.contentWindow.location = url;
|
||||
}
|
||||
|
||||
function checkLoadEventForTest8() {
|
||||
gTestBrowser.removeEventListener("load", checkLoadEventForTest8, true);
|
||||
|
||||
var expected = "image loaded"
|
||||
waitForCondition(
|
||||
function() content.document.getElementById('mctestdiv').innerHTML == expected,
|
||||
test9, "Error: Waited too long for status in Test 8!",
|
||||
"OK: Expected result in innerHTML for Test8!");
|
||||
// Go back online
|
||||
Services.io.offline = false;
|
||||
}
|
||||
|
||||
//------------------------ Test 9 ------------------------------
|
||||
// HTTPS page loading insecure cached image that went through a double redirect
|
||||
// Assuming test 7 succeeded, the image has already been loaded once
|
||||
// and hence should be cached per the sjs cache-control header
|
||||
// Going into offline mode to ensure we are loading from the cache.
|
||||
function test9() {
|
||||
gTestBrowser.addEventListener("load", checkLoadEventForTest9, true);
|
||||
// Go into offline mode
|
||||
Services.io.offline = true;
|
||||
var url = gHttpsTestRoot + "test_mcb_double_redirect_image.html"
|
||||
gTestBrowser.contentWindow.location = url;
|
||||
}
|
||||
|
||||
function checkLoadEventForTest9() {
|
||||
gTestBrowser.removeEventListener("load", checkLoadEventForTest9, true);
|
||||
|
||||
var expected = "image blocked"
|
||||
waitForCondition(
|
||||
function() content.document.getElementById('mctestdiv').innerHTML == expected,
|
||||
cleanUpAfterTests, "Error: Waited too long for status in Test 9!",
|
||||
"OK: Expected result in innerHTML for Test9!");
|
||||
// Go back online
|
||||
Services.io.offline = false;
|
||||
}
|
||||
|
||||
//------------------------ SETUP ------------------------------
|
||||
|
||||
function test() {
|
||||
// Performing async calls, e.g. 'onload', we have to wait till all of them finished
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Store original preferences so we can restore settings after testing
|
||||
origBlockActive = Services.prefs.getBoolPref(PREF_ACTIVE);
|
||||
origBlockDisplay = Services.prefs.getBoolPref(PREF_DISPLAY);
|
||||
Services.prefs.setBoolPref(PREF_ACTIVE, true);
|
||||
Services.prefs.setBoolPref(PREF_DISPLAY, true);
|
||||
|
||||
var newTab = gBrowser.addTab();
|
||||
gBrowser.selectedTab = newTab;
|
||||
gTestBrowser = gBrowser.selectedBrowser;
|
||||
newTab.linkedBrowser.stop();
|
||||
|
||||
executeSoon(test1);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test 7-9 for Bug 1082837 - See file browser_mcb_redirect.js for description.
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1082837
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bug 1082837</title>
|
||||
<script>
|
||||
function image_loaded() {
|
||||
document.getElementById("mctestdiv").innerHTML = "image loaded";
|
||||
}
|
||||
function image_blocked() {
|
||||
document.getElementById("mctestdiv").innerHTML = "image blocked";
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mctestdiv"></div>
|
||||
<img src="https://example.com/browser/browser/base/content/test/general/test_mcb_redirect.sjs?image_redirect_http_sjs" onload="image_loaded()" onerror="image_blocked()" ></image>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test 1 for Bug 418354 - See file browser_mcb_redirect.js for description.
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=418354
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bug 418354</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mctestdiv">script blocked</div>
|
||||
<script src="https://example.com/browser/browser/base/content/test/general/test_mcb_redirect.sjs?script" ></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,22 @@
|
||||
function handleRequest(request, response) {
|
||||
var page = "<!DOCTYPE html><html><body>bug 418354 and bug 1082837</body></html>";
|
||||
|
||||
if (request.queryString === "script") {
|
||||
var redirect = "http://example.com/browser/browser/base/content/test/general/test_mcb_redirect.js";
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
} else if (request.queryString === "image_http") {
|
||||
var redirect = "http://example.com/tests/image/test/mochitest/blue.png";
|
||||
response.setHeader("Cache-Control", "max-age=3600", false);
|
||||
} else if (request.queryString === "image_redirect_http_sjs") {
|
||||
var redirect = "http://example.com/browser/browser/base/content/test/general/test_mcb_redirect.sjs?image_redirect_https";
|
||||
response.setHeader("Cache-Control", "max-age=3600", false);
|
||||
} else if (request.queryString === "image_redirect_https") {
|
||||
var redirect = "https://example.com/tests/image/test/mochitest/blue.png";
|
||||
response.setHeader("Cache-Control", "max-age=3600", false);
|
||||
}
|
||||
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
response.setStatusLine(request.httpVersion, "302", "Found");
|
||||
response.setHeader("Location", redirect, false);
|
||||
response.write(page);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test 3-6 for Bug 1082837 - See file browser_mcb_redirect.js for description.
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1082837
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bug 1082837</title>
|
||||
<script>
|
||||
function image_loaded() {
|
||||
document.getElementById("mctestdiv").innerHTML = "image loaded";
|
||||
}
|
||||
function image_blocked() {
|
||||
document.getElementById("mctestdiv").innerHTML = "image blocked";
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mctestdiv"></div>
|
||||
<img src="https://example.com/browser/browser/base/content/test/general/test_mcb_redirect.sjs?image_http" onload="image_loaded()" onerror="image_blocked()" ></image>
|
||||
</body>
|
||||
</html>
|
||||
+8
-30
@@ -28,7 +28,7 @@ OriginAttributes::CreateSuffix(nsACString& aStr) const
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
|
||||
|
||||
nsRefPtr<URLSearchParams> usp = new URLSearchParams();
|
||||
nsRefPtr<URLSearchParams> usp = new URLSearchParams(nullptr);
|
||||
nsAutoString value;
|
||||
|
||||
if (mAppId != nsIScriptSecurityManager::NO_APP_ID) {
|
||||
@@ -108,8 +108,8 @@ OriginAttributes::PopulateFromSuffix(const nsACString& aStr)
|
||||
return false;
|
||||
}
|
||||
|
||||
nsRefPtr<URLSearchParams> usp = new URLSearchParams();
|
||||
usp->ParseInput(Substring(aStr, 1, aStr.Length() - 1), nullptr);
|
||||
nsRefPtr<URLSearchParams> usp = new URLSearchParams(nullptr);
|
||||
usp->ParseInput(Substring(aStr, 1, aStr.Length() - 1));
|
||||
|
||||
PopulateFromSuffixIterator iterator(this);
|
||||
return usp->ForEach(iterator);
|
||||
@@ -130,14 +130,14 @@ BasePrincipal::GetOrigin(nsACString& aOrigin)
|
||||
bool
|
||||
BasePrincipal::Subsumes(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aOther, "The caller is performing a nonsensical security check!");
|
||||
MOZ_ASSERT(aOther);
|
||||
return SubsumesInternal(aOther, aConsideration);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
BasePrincipal::Equals(nsIPrincipal *aOther, bool *aResult)
|
||||
{
|
||||
|
||||
NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
|
||||
*aResult = Subsumes(aOther, DontConsiderDocumentDomain) &&
|
||||
Cast(aOther)->Subsumes(this, DontConsiderDocumentDomain);
|
||||
return NS_OK;
|
||||
@@ -146,6 +146,7 @@ BasePrincipal::Equals(nsIPrincipal *aOther, bool *aResult)
|
||||
NS_IMETHODIMP
|
||||
BasePrincipal::EqualsConsideringDomain(nsIPrincipal *aOther, bool *aResult)
|
||||
{
|
||||
NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
|
||||
*aResult = Subsumes(aOther, ConsiderDocumentDomain) &&
|
||||
Cast(aOther)->Subsumes(this, ConsiderDocumentDomain);
|
||||
return NS_OK;
|
||||
@@ -154,6 +155,7 @@ BasePrincipal::EqualsConsideringDomain(nsIPrincipal *aOther, bool *aResult)
|
||||
NS_IMETHODIMP
|
||||
BasePrincipal::Subsumes(nsIPrincipal *aOther, bool *aResult)
|
||||
{
|
||||
NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
|
||||
*aResult = Subsumes(aOther, DontConsiderDocumentDomain);
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -161,6 +163,7 @@ BasePrincipal::Subsumes(nsIPrincipal *aOther, bool *aResult)
|
||||
NS_IMETHODIMP
|
||||
BasePrincipal::SubsumesConsideringDomain(nsIPrincipal *aOther, bool *aResult)
|
||||
{
|
||||
NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
|
||||
*aResult = Subsumes(aOther, ConsiderDocumentDomain);
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -310,29 +313,4 @@ BasePrincipal::CreateCodebasePrincipal(nsIURI* aURI, OriginAttributes& aAttrs)
|
||||
return codebase.forget();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
BasePrincipal::IsCodebasePrincipal(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
|
||||
bool isNullPrincipal = true;
|
||||
nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isNullPrincipal || nsContentUtils::IsSystemPrincipal(aPrincipal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// No expanded principals.
|
||||
nsCOMPtr<nsIExpandedPrincipal> expandedPrincipal =
|
||||
do_QueryInterface(aPrincipal);
|
||||
if (expandedPrincipal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsJSPrincipals.h"
|
||||
|
||||
#include "mozilla/dom/SystemDictionariesBinding.h"
|
||||
#include "mozilla/dom/ChromeUtilsBinding.h"
|
||||
|
||||
class nsIContentSecurityPolicy;
|
||||
class nsIObjectOutputStream;
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
|
||||
virtual bool IsOnCSSUnprefixingWhitelist() override { return false; }
|
||||
|
||||
static bool IsCodebasePrincipal(nsIPrincipal* aPrincipal);
|
||||
virtual bool IsCodebasePrincipal() const { return false; };
|
||||
|
||||
static BasePrincipal* Cast(nsIPrincipal* aPrin) { return static_cast<BasePrincipal*>(aPrin); }
|
||||
static already_AddRefed<BasePrincipal> CreateCodebasePrincipal(nsIURI* aURI, OriginAttributes& aAttrs);
|
||||
|
||||
@@ -29,6 +29,7 @@ public:
|
||||
NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal) override;
|
||||
NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
|
||||
virtual bool IsOnCSSUnprefixingWhitelist() override;
|
||||
bool IsCodebasePrincipal() const override { return true; }
|
||||
nsresult GetOriginInternal(nsACString& aOrigin) override;
|
||||
|
||||
nsPrincipal();
|
||||
|
||||
+2
-19
@@ -578,21 +578,6 @@ Link::SearchParams()
|
||||
return mSearchParams;
|
||||
}
|
||||
|
||||
void
|
||||
Link::SetSearchParams(URLSearchParams& aSearchParams)
|
||||
{
|
||||
if (mSearchParams) {
|
||||
mSearchParams->RemoveObserver(this);
|
||||
}
|
||||
|
||||
mSearchParams = &aSearchParams;
|
||||
mSearchParams->AddObserver(this);
|
||||
|
||||
nsAutoString search;
|
||||
mSearchParams->Serialize(search);
|
||||
SetSearchInternal(search);
|
||||
}
|
||||
|
||||
void
|
||||
Link::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
|
||||
{
|
||||
@@ -621,15 +606,14 @@ Link::UpdateURLSearchParams()
|
||||
}
|
||||
}
|
||||
|
||||
mSearchParams->ParseInput(search, this);
|
||||
mSearchParams->ParseInput(search);
|
||||
}
|
||||
|
||||
void
|
||||
Link::CreateSearchParamsIfNeeded()
|
||||
{
|
||||
if (!mSearchParams) {
|
||||
mSearchParams = new URLSearchParams();
|
||||
mSearchParams->AddObserver(this);
|
||||
mSearchParams = new URLSearchParams(this);
|
||||
UpdateURLSearchParams();
|
||||
}
|
||||
}
|
||||
@@ -638,7 +622,6 @@ void
|
||||
Link::Unlink()
|
||||
{
|
||||
if (mSearchParams) {
|
||||
mSearchParams->RemoveObserver(this);
|
||||
mSearchParams = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,6 @@ public:
|
||||
void SetHostname(const nsAString &aHostname, ErrorResult& aError);
|
||||
void SetPathname(const nsAString &aPathname, ErrorResult& aError);
|
||||
void SetSearch(const nsAString &aSearch, ErrorResult& aError);
|
||||
void SetSearchParams(mozilla::dom::URLSearchParams& aSearchParams);
|
||||
void SetPort(const nsAString &aPort, ErrorResult& aError);
|
||||
void SetHash(const nsAString &aHash, ErrorResult& aError);
|
||||
void GetOrigin(nsAString &aOrigin, ErrorResult& aError);
|
||||
|
||||
+2
-20
@@ -27,7 +27,6 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(URL)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(URL)
|
||||
if (tmp->mSearchParams) {
|
||||
tmp->mSearchParams->RemoveObserver(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSearchParams)
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
@@ -358,7 +357,7 @@ URL::UpdateURLSearchParams()
|
||||
}
|
||||
}
|
||||
|
||||
mSearchParams->ParseInput(search, this);
|
||||
mSearchParams->ParseInput(search);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -484,22 +483,6 @@ URL::SearchParams()
|
||||
return mSearchParams;
|
||||
}
|
||||
|
||||
void
|
||||
URL::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);
|
||||
}
|
||||
|
||||
void
|
||||
URL::GetHash(nsAString& aHash, ErrorResult& aRv) const
|
||||
{
|
||||
@@ -534,8 +517,7 @@ void
|
||||
URL::CreateSearchParamsIfNeeded()
|
||||
{
|
||||
if (!mSearchParams) {
|
||||
mSearchParams = new URLSearchParams();
|
||||
mSearchParams->AddObserver(this);
|
||||
mSearchParams = new URLSearchParams(this);
|
||||
UpdateURLSearchParams();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,8 +119,6 @@ public:
|
||||
|
||||
URLSearchParams* SearchParams();
|
||||
|
||||
void SetSearchParams(URLSearchParams& aSearchParams);
|
||||
|
||||
void GetHash(nsAString& aRetval, ErrorResult& aRv) const;
|
||||
|
||||
void SetHash(const nsAString& aArg, ErrorResult& aRv);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URLSearchParams, mObservers)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URLSearchParams, mObserver)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams)
|
||||
|
||||
@@ -21,7 +21,8 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
URLSearchParams::URLSearchParams()
|
||||
URLSearchParams::URLSearchParams(URLSearchParamsObserver* aObserver)
|
||||
: mObserver(aObserver)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -41,8 +42,8 @@ URLSearchParams::Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aInit,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsRefPtr<URLSearchParams> sp = new URLSearchParams();
|
||||
sp->ParseInput(NS_ConvertUTF16toUTF8(aInit), nullptr);
|
||||
nsRefPtr<URLSearchParams> sp = new URLSearchParams(nullptr);
|
||||
sp->ParseInput(NS_ConvertUTF16toUTF8(aInit));
|
||||
return sp.forget();
|
||||
}
|
||||
|
||||
@@ -51,14 +52,13 @@ URLSearchParams::Constructor(const GlobalObject& aGlobal,
|
||||
URLSearchParams& aInit,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsRefPtr<URLSearchParams> sp = new URLSearchParams();
|
||||
nsRefPtr<URLSearchParams> sp = new URLSearchParams(nullptr);
|
||||
sp->mSearchParams = aInit.mSearchParams;
|
||||
return sp.forget();
|
||||
}
|
||||
|
||||
void
|
||||
URLSearchParams::ParseInput(const nsACString& aInput,
|
||||
URLSearchParamsObserver* aObserver)
|
||||
URLSearchParams::ParseInput(const nsACString& aInput)
|
||||
{
|
||||
// Remove all the existing data before parsing a new input.
|
||||
DeleteAll();
|
||||
@@ -108,8 +108,6 @@ URLSearchParams::ParseInput(const nsACString& aInput,
|
||||
|
||||
AppendInternal(decodedName, decodedValue);
|
||||
}
|
||||
|
||||
NotifyObservers(aObserver);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -208,27 +206,6 @@ URLSearchParams::ConvertString(const nsACString& aInput, nsAString& aOutput)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
URLSearchParams::AddObserver(URLSearchParamsObserver* aObserver)
|
||||
{
|
||||
MOZ_ASSERT(aObserver);
|
||||
MOZ_ASSERT(!mObservers.Contains(aObserver));
|
||||
mObservers.AppendElement(aObserver);
|
||||
}
|
||||
|
||||
void
|
||||
URLSearchParams::RemoveObserver(URLSearchParamsObserver* aObserver)
|
||||
{
|
||||
MOZ_ASSERT(aObserver);
|
||||
mObservers.RemoveElement(aObserver);
|
||||
}
|
||||
|
||||
void
|
||||
URLSearchParams::RemoveObservers()
|
||||
{
|
||||
mObservers.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
|
||||
{
|
||||
@@ -280,14 +257,14 @@ URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
|
||||
|
||||
param->mValue = aValue;
|
||||
|
||||
NotifyObservers(nullptr);
|
||||
NotifyObserver();
|
||||
}
|
||||
|
||||
void
|
||||
URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
|
||||
{
|
||||
AppendInternal(aName, aValue);
|
||||
NotifyObservers(nullptr);
|
||||
NotifyObserver();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -324,7 +301,7 @@ URLSearchParams::Delete(const nsAString& aName)
|
||||
}
|
||||
|
||||
if (found) {
|
||||
NotifyObservers(nullptr);
|
||||
NotifyObserver();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,12 +357,10 @@ URLSearchParams::Serialize(nsAString& aValue) const
|
||||
}
|
||||
|
||||
void
|
||||
URLSearchParams::NotifyObservers(URLSearchParamsObserver* aExceptObserver)
|
||||
URLSearchParams::NotifyObserver()
|
||||
{
|
||||
for (uint32_t i = 0; i < mObservers.Length(); ++i) {
|
||||
if (mObservers[i] != aExceptObserver) {
|
||||
mObservers[i]->URLSearchParamsUpdated(this);
|
||||
}
|
||||
if (mObserver) {
|
||||
mObserver->URLSearchParamsUpdated(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(URLSearchParams)
|
||||
|
||||
URLSearchParams();
|
||||
explicit URLSearchParams(URLSearchParamsObserver* aObserver);
|
||||
|
||||
// WebIDL methods
|
||||
nsISupports* GetParentObject() const
|
||||
@@ -59,12 +59,7 @@ public:
|
||||
Constructor(const GlobalObject& aGlobal, URLSearchParams& aInit,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void ParseInput(const nsACString& aInput,
|
||||
URLSearchParamsObserver* aObserver);
|
||||
|
||||
void AddObserver(URLSearchParamsObserver* aObserver);
|
||||
void RemoveObserver(URLSearchParamsObserver* aObserver);
|
||||
void RemoveObservers();
|
||||
void ParseInput(const nsACString& aInput);
|
||||
|
||||
void Serialize(nsAString& aValue) const;
|
||||
|
||||
@@ -113,7 +108,7 @@ private:
|
||||
void DecodeString(const nsACString& aInput, nsAString& aOutput);
|
||||
void ConvertString(const nsACString& aInput, nsAString& aOutput);
|
||||
|
||||
void NotifyObservers(URLSearchParamsObserver* aExceptObserver);
|
||||
void NotifyObserver();
|
||||
|
||||
struct Param
|
||||
{
|
||||
@@ -123,7 +118,7 @@ private:
|
||||
|
||||
nsTArray<Param> mSearchParams;
|
||||
|
||||
nsTArray<nsRefPtr<URLSearchParamsObserver>> mObservers;
|
||||
nsRefPtr<URLSearchParamsObserver> mObserver;
|
||||
nsCOMPtr<nsIUnicodeDecoder> mDecoder;
|
||||
};
|
||||
|
||||
|
||||
@@ -8008,3 +8008,54 @@ nsContentUtils::GetWindowRoot(nsIDocument* aDoc)
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
|
||||
nsIDocument* aDoc,
|
||||
nsIHttpChannel* aChannel)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aPrincipal);
|
||||
NS_ENSURE_ARG_POINTER(aChannel);
|
||||
|
||||
nsCOMPtr<nsIURI> principalURI;
|
||||
|
||||
if (IsSystemPrincipal(aPrincipal)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
aPrincipal->GetURI(getter_AddRefs(principalURI));
|
||||
|
||||
if (!aDoc) {
|
||||
return aChannel->SetReferrerWithPolicy(principalURI, net::RP_Default);
|
||||
}
|
||||
|
||||
// If it weren't for history.push/replaceState, we could just use the
|
||||
// principal's URI here. But since we want changes to the URI effected
|
||||
// by push/replaceState to be reflected in the XHR referrer, we have to
|
||||
// be more clever.
|
||||
//
|
||||
// If the document's original URI (before any push/replaceStates) matches
|
||||
// our principal, then we use the document's current URI (after
|
||||
// push/replaceStates). Otherwise (if the document is, say, a data:
|
||||
// URI), we just use the principal's URI.
|
||||
nsCOMPtr<nsIURI> docCurURI = aDoc->GetDocumentURI();
|
||||
nsCOMPtr<nsIURI> docOrigURI = aDoc->GetOriginalURI();
|
||||
|
||||
nsCOMPtr<nsIURI> referrerURI;
|
||||
|
||||
if (principalURI && docCurURI && docOrigURI) {
|
||||
bool equal = false;
|
||||
principalURI->Equals(docOrigURI, &equal);
|
||||
if (equal) {
|
||||
referrerURI = docCurURI;
|
||||
}
|
||||
}
|
||||
|
||||
if (!referrerURI) {
|
||||
referrerURI = principalURI;
|
||||
}
|
||||
|
||||
net::ReferrerPolicy referrerPolicy = aDoc->GetReferrerPolicy();
|
||||
return aChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
|
||||
}
|
||||
@@ -2489,6 +2489,25 @@ public:
|
||||
|
||||
static already_AddRefed<nsPIWindowRoot> GetWindowRoot(nsIDocument* aDoc);
|
||||
|
||||
/*
|
||||
* Implements step 3.1 and 3.3 of the Determine request's Referrer algorithm
|
||||
* from the Referrer Policy specification.
|
||||
*
|
||||
* The referrer policy of the document is applied by Necko when using
|
||||
* channels.
|
||||
*
|
||||
* For documents representing an iframe srcdoc attribute, the document sets
|
||||
* its own URI correctly, so this method simply uses the document's original
|
||||
* or current URI as appropriate.
|
||||
*
|
||||
* aDoc may be null.
|
||||
*
|
||||
* https://w3c.github.io/webappsec/specs/referrer-policy/#determine-requests-referrer
|
||||
*/
|
||||
static nsresult SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
|
||||
nsIDocument* aDoc,
|
||||
nsIHttpChannel* aChannel);
|
||||
|
||||
private:
|
||||
static bool InitializeEventTable();
|
||||
|
||||
|
||||
@@ -2716,50 +2716,10 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
||||
httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
|
||||
|
||||
if (!IsSystemXHR()) {
|
||||
// Get the referrer for the request.
|
||||
//
|
||||
// If it weren't for history.push/replaceState, we could just use the
|
||||
// principal's URI here. But since we want changes to the URI effected
|
||||
// by push/replaceState to be reflected in the XHR referrer, we have to
|
||||
// be more clever.
|
||||
//
|
||||
// If the document's original URI (before any push/replaceStates) matches
|
||||
// our principal, then we use the document's current URI (after
|
||||
// push/replaceStates). Otherwise (if the document is, say, a data:
|
||||
// URI), we just use the principal's URI.
|
||||
|
||||
nsCOMPtr<nsIURI> principalURI;
|
||||
mPrincipal->GetURI(getter_AddRefs(principalURI));
|
||||
|
||||
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIDocument> doc =
|
||||
nsContentUtils::GetDocumentFromScriptContext(sc);
|
||||
|
||||
nsCOMPtr<nsIURI> docCurURI;
|
||||
nsCOMPtr<nsIURI> docOrigURI;
|
||||
net::ReferrerPolicy referrerPolicy = net::RP_Default;
|
||||
|
||||
if (doc) {
|
||||
docCurURI = doc->GetDocumentURI();
|
||||
docOrigURI = doc->GetOriginalURI();
|
||||
referrerPolicy = doc->GetReferrerPolicy();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> referrerURI;
|
||||
|
||||
if (principalURI && docCurURI && docOrigURI) {
|
||||
bool equal = false;
|
||||
principalURI->Equals(docOrigURI, &equal);
|
||||
if (equal) {
|
||||
referrerURI = docCurURI;
|
||||
}
|
||||
}
|
||||
|
||||
if (!referrerURI)
|
||||
referrerURI = principalURI;
|
||||
|
||||
httpChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
|
||||
nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
|
||||
nsCOMPtr<nsIDocument> doc = owner ? owner->GetExtantDoc() : nullptr;
|
||||
nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, doc,
|
||||
httpChannel);
|
||||
}
|
||||
|
||||
// Some extensions override the http protocol handler and provide their own
|
||||
|
||||
@@ -124,28 +124,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836
|
||||
url.searchParams.set('e', 'f');
|
||||
ok(url.href.indexOf('e=f') != 1, 'URL right');
|
||||
|
||||
var u = new URLSearchParams();
|
||||
u.append('foo', 'bar');
|
||||
url.searchParams = u;
|
||||
is(url.searchParams, u, "URL.searchParams is the same object");
|
||||
is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')");
|
||||
is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
|
||||
|
||||
try {
|
||||
url.searchParams = null;
|
||||
ok(false, "URLSearchParams is not nullable");
|
||||
} catch(e) {
|
||||
ok(true, "URLSearchParams is not nullable");
|
||||
}
|
||||
|
||||
var url2 = new URL('http://www.example.net?e=f');
|
||||
url.searchParams = url2.searchParams;
|
||||
is(url.searchParams, url2.searchParams, "URL.searchParams is not the same object");
|
||||
is(url.searchParams.get('e'), 'f', "URL.searchParams.get('e')");
|
||||
|
||||
url.href = "http://www.example.net?bar=foo";
|
||||
is(url.searchParams.get('bar'), 'foo', "URL.searchParams.get('bar')");
|
||||
|
||||
runTest();
|
||||
}
|
||||
|
||||
@@ -160,31 +138,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836
|
||||
e.searchParams.set('e', 'f');
|
||||
ok(e.href.indexOf('e=f') != 1, 'e is right');
|
||||
|
||||
var u = new URLSearchParams();
|
||||
u.append('foo', 'bar');
|
||||
e.searchParams = u;
|
||||
is(e.searchParams, u, "e.searchParams is the same object");
|
||||
is(e.searchParams.get('foo'), 'bar', "e.searchParams.get('foo')");
|
||||
is(e.href, 'http://www.example.net/?foo=bar', 'e is right');
|
||||
|
||||
try {
|
||||
e.searchParams = null;
|
||||
ok(false, "URLSearchParams is not nullable");
|
||||
} catch(e) {
|
||||
ok(true, "URLSearchParams is not nullable");
|
||||
}
|
||||
|
||||
var url2 = new URL('http://www.example.net?e=f');
|
||||
e.searchParams = url2.searchParams;
|
||||
is(e.searchParams, url2.searchParams, "e.searchParams is not the same object");
|
||||
is(e.searchParams.get('e'), 'f', "e.searchParams.get('e')");
|
||||
|
||||
e.href = "http://www.example.net?bar=foo";
|
||||
is(e.searchParams.get('bar'), 'foo', "e.searchParams.get('bar')");
|
||||
|
||||
e.setAttribute('href', "http://www.example.net?bar2=foo2");
|
||||
is(e.searchParams.get('bar2'), 'foo2', "e.searchParams.get('bar2')");
|
||||
|
||||
runTest();
|
||||
}
|
||||
|
||||
@@ -195,11 +148,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836
|
||||
[ '\u0541', '%D5%81'] ];
|
||||
|
||||
for (var i = 0; i < encoding.length; ++i) {
|
||||
var a = new URLSearchParams();
|
||||
a.set('a', encoding[i][0]);
|
||||
|
||||
var url = new URL('http://www.example.net');
|
||||
url.searchParams = a;
|
||||
url.searchParams.set('a', encoding[i][0]);
|
||||
is(url.href, 'http://www.example.net/?a=' + encoding[i][1]);
|
||||
|
||||
var url2 = new URL(url.href);
|
||||
@@ -209,45 +159,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836
|
||||
runTest();
|
||||
}
|
||||
|
||||
function testMultiURL() {
|
||||
var a = new URL('http://www.example.net?a=b&c=d');
|
||||
var b = new URL('http://www.example.net?e=f');
|
||||
var c = document.createElement('a');
|
||||
var d = document.createElement('area');
|
||||
ok(a.searchParams.has('a'), "a.searchParams.has('a')");
|
||||
ok(a.searchParams.has('c'), "a.searchParams.has('c')");
|
||||
ok(b.searchParams.has('e'), "b.searchParams.has('e')");
|
||||
ok(c.searchParams, "c.searchParams");
|
||||
ok(d.searchParams, "d.searchParams");
|
||||
|
||||
var u = new URLSearchParams();
|
||||
a.searchParams = b.searchParams = c.searchParams = d.searchParams = u;
|
||||
is(a.searchParams, u, "a.searchParams === u");
|
||||
is(b.searchParams, u, "b.searchParams === u");
|
||||
is(c.searchParams, u, "c.searchParams === u");
|
||||
is(d.searchParams, u, "d.searchParams === u");
|
||||
ok(!a.searchParams.has('a'), "!a.searchParams.has('a')");
|
||||
ok(!a.searchParams.has('c'), "!a.searchParams.has('c')");
|
||||
ok(!b.searchParams.has('e'), "!b.searchParams.has('e')");
|
||||
|
||||
u.append('foo', 'bar');
|
||||
is(a.searchParams.get('foo'), 'bar', "a has foo=bar");
|
||||
is(b.searchParams.get('foo'), 'bar', "b has foo=bar");
|
||||
is(c.searchParams.get('foo'), 'bar', "c has foo=bar");
|
||||
is(d.searchParams.get('foo'), 'bar', "d has foo=bar");
|
||||
is(a + "", b + "", "stringify a == b");
|
||||
is(c.searchParams + "", b.searchParams + "", "stringify c.searchParams == b.searchParams");
|
||||
is(d.searchParams + "", b.searchParams + "", "stringify d.searchParams == b.searchParams");
|
||||
|
||||
a.search = "?bar=foo";
|
||||
is(a.searchParams.get('bar'), 'foo', "a has bar=foo");
|
||||
is(b.searchParams.get('bar'), 'foo', "b has bar=foo");
|
||||
is(c.searchParams.get('bar'), 'foo', "c has bar=foo");
|
||||
is(d.searchParams.get('bar'), 'foo', "d has bar=foo");
|
||||
|
||||
runTest();
|
||||
}
|
||||
|
||||
function testOrdering() {
|
||||
var a = new URLSearchParams("a=1&a=2&b=3&c=4&c=5&a=6");
|
||||
is(a.toString(), "a=1&a=2&b=3&c=4&c=5&a=6", "Order is correct");
|
||||
@@ -334,7 +245,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836
|
||||
function() { testElement(document.getElementById('anchor')) },
|
||||
function() { testElement(document.getElementById('area')) },
|
||||
testEncoding,
|
||||
testMultiURL,
|
||||
testOrdering,
|
||||
testDelete,
|
||||
testGetNULL,
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
<script>
|
||||
test(function() {
|
||||
var props = Object.getOwnPropertyNames(Object.prototype);
|
||||
// getOwnPropertyNames intentionally filters out the non-standard
|
||||
// "__proto__" property.
|
||||
props.push("__proto__");
|
||||
// If you change this list, make sure it continues to match the list in
|
||||
// Codegen.py's CGDictionary.getMemberDefinition method.
|
||||
var expected = [
|
||||
|
||||
+2
-59
@@ -217,11 +217,6 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
|
||||
|
||||
nsRefPtr<InternalRequest> r = request->GetInternalRequest();
|
||||
|
||||
aRv = UpdateRequestReferrer(aGlobal, r);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
|
||||
nsCOMPtr<nsIDocument> doc;
|
||||
@@ -398,58 +393,6 @@ WorkerFetchResolver::OnResponseEnd()
|
||||
}
|
||||
}
|
||||
|
||||
// This method sets the request's referrerURL, as specified by the "determine
|
||||
// request's referrer" steps from Referrer Policy [1].
|
||||
// The actual referrer policy and stripping is dealt with by HttpBaseChannel,
|
||||
// this always sets the full API referrer URL of the relevant global if it is
|
||||
// not already a url or no-referrer.
|
||||
// [1]: https://w3c.github.io/webappsec/specs/referrer-policy/#determine-requests-referrer
|
||||
nsresult
|
||||
UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest)
|
||||
{
|
||||
nsAutoString originalReferrer;
|
||||
aRequest->GetReferrer(originalReferrer);
|
||||
// If it is no-referrer ("") or a URL, don't modify.
|
||||
if (!originalReferrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
|
||||
if (window) {
|
||||
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
|
||||
if (doc) {
|
||||
nsAutoString referrer;
|
||||
doc->GetReferrer(referrer);
|
||||
aRequest->SetReferrer(referrer);
|
||||
}
|
||||
} else if (NS_IsMainThread()) {
|
||||
// Pull the principal from the global for non-worker scripts.
|
||||
nsIPrincipal *principal = aGlobal->PrincipalOrNull();
|
||||
bool isNull;
|
||||
// Only set the referrer if the principal is present,
|
||||
// and the principal is not null or the system principal.
|
||||
if (principal &&
|
||||
NS_SUCCEEDED(principal->GetIsNullPrincipal(&isNull)) && !isNull &&
|
||||
!nsContentUtils::IsSystemPrincipal(principal)) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
if (NS_SUCCEEDED(principal->GetURI(getter_AddRefs(uri))) && uri) {
|
||||
nsAutoCString referrer;
|
||||
if (NS_SUCCEEDED(uri->GetSpec(referrer))) {
|
||||
aRequest->SetReferrer(NS_ConvertUTF8toUTF16(referrer));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
worker->AssertIsOnWorkerThread();
|
||||
WorkerPrivate::LocationInfo& info = worker->GetLocationInfo();
|
||||
aRequest->SetReferrer(NS_ConvertUTF8toUTF16(info.mHref));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
namespace {
|
||||
nsresult
|
||||
ExtractFromArrayBuffer(const ArrayBuffer& aBuffer,
|
||||
@@ -1600,8 +1543,8 @@ FetchBody<Derived>::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength
|
||||
}
|
||||
|
||||
if (isValidUrlEncodedMimeType) {
|
||||
nsRefPtr<URLSearchParams> params = new URLSearchParams();
|
||||
params->ParseInput(data, /* aObserver */ nullptr);
|
||||
nsRefPtr<URLSearchParams> params = new URLSearchParams(nullptr);
|
||||
params->ParseInput(data);
|
||||
|
||||
nsRefPtr<nsFormData> fd = new nsFormData(DerivedClass()->GetParentObject());
|
||||
FillFormIterator iterator(fd);
|
||||
|
||||
+27
-11
@@ -416,21 +416,37 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
|
||||
// Step 2. Set the referrer.
|
||||
nsAutoString referrer;
|
||||
mRequest->GetReferrer(referrer);
|
||||
// The referrer should have already been resolved to a URL by the caller.
|
||||
MOZ_ASSERT(!referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR));
|
||||
if (!referrer.IsEmpty()) {
|
||||
nsCOMPtr<nsIURI> refURI;
|
||||
rv = NS_NewURI(getter_AddRefs(refURI), referrer, nullptr, nullptr);
|
||||
if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
|
||||
rv = nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal,
|
||||
mDocument,
|
||||
httpChan);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
} else if (referrer.IsEmpty()) {
|
||||
rv = httpChan->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
} else {
|
||||
// From "Determine request's Referrer" step 3
|
||||
// "If request's referrer is a URL, let referrerSource be request's
|
||||
// referrer."
|
||||
//
|
||||
// This allows ServiceWorkers to function transparently when the referrer
|
||||
// of the intercepted request is already set.
|
||||
nsCOMPtr<nsIURI> referrerURI;
|
||||
rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
|
||||
net::ReferrerPolicy referrerPolicy = net::RP_Default;
|
||||
if (mDocument) {
|
||||
referrerPolicy = mDocument->GetReferrerPolicy();
|
||||
}
|
||||
|
||||
rv = httpChan->SetReferrerWithPolicy(refURI, referrerPolicy);
|
||||
// FIXME(nsm): Can we assert that this case can only happen in
|
||||
// ServiceWorkers and assume null mDocument?
|
||||
rv =
|
||||
httpChan->SetReferrerWithPolicy(nullptr,
|
||||
mDocument ? mDocument->GetReferrerPolicy() :
|
||||
net::RP_Default);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
|
||||
@@ -158,7 +158,6 @@ public:
|
||||
using Link::SetHash;
|
||||
|
||||
// The Link::GetSearchParams is OK for us
|
||||
// The Link::SetSearchParams is OK for us
|
||||
|
||||
bool NoHref() const
|
||||
{
|
||||
|
||||
@@ -1250,6 +1250,25 @@ function testRedirects() {
|
||||
return Promise.all(fetches);
|
||||
}
|
||||
|
||||
function testReferrer() {
|
||||
var referrer;
|
||||
if (self && self.location) {
|
||||
referrer = self.location.href;
|
||||
} else {
|
||||
referrer = document.documentURI;
|
||||
}
|
||||
|
||||
var dict = {
|
||||
'Referer': referrer
|
||||
};
|
||||
return fetch(corsServerPath + "headers=" + dict.toSource()).then(function(res) {
|
||||
is(res.status, 200, "expected correct referrer header to be sent");
|
||||
dump(res.statusText);
|
||||
}, function(e) {
|
||||
ok(false, "expected correct referrer header to be sent");
|
||||
});
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
testNoCorsCtor();
|
||||
|
||||
@@ -1260,5 +1279,6 @@ function runTest() {
|
||||
.then(testSameOriginCredentials)
|
||||
.then(testCrossOriginCredentials)
|
||||
.then(testRedirects)
|
||||
.then(testReferrer)
|
||||
// Put more promise based tests here.
|
||||
}
|
||||
|
||||
@@ -61,3 +61,17 @@ dictionary HeapSnapshotBoundaries {
|
||||
object debugger;
|
||||
boolean runtime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used by principals and the script security manager to represent origin
|
||||
* attributes.
|
||||
*
|
||||
* IMPORTANT: If you add any members here, you need to update the
|
||||
* 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;
|
||||
boolean inBrowser = false;
|
||||
};
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*
|
||||
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Used by principals and the script security manager to represent origin
|
||||
* attributes.
|
||||
*
|
||||
* IMPORTANT: If you add any members here, you need to update the
|
||||
* 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;
|
||||
boolean inBrowser = false;
|
||||
};
|
||||
@@ -51,5 +51,5 @@ interface URLUtils {
|
||||
[NoInterfaceObject,
|
||||
Exposed=(Window, Worker)]
|
||||
interface URLUtilsSearchParams {
|
||||
attribute URLSearchParams searchParams;
|
||||
readonly attribute URLSearchParams searchParams;
|
||||
};
|
||||
|
||||
@@ -510,7 +510,6 @@ WEBIDL_FILES = [
|
||||
'SVGViewElement.webidl',
|
||||
'SVGZoomAndPan.webidl',
|
||||
'SVGZoomEvent.webidl',
|
||||
'SystemDictionaries.webidl',
|
||||
'SystemUpdate.webidl',
|
||||
'Telephony.webidl',
|
||||
'TelephonyCall.webidl',
|
||||
|
||||
@@ -238,7 +238,7 @@ PopulateRegistrationData(nsIPrincipal* aPrincipal,
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
MOZ_ASSERT(aRegistration);
|
||||
|
||||
if (NS_WARN_IF(!BasePrincipal::IsCodebasePrincipal(aPrincipal))) {
|
||||
if (NS_WARN_IF(!BasePrincipal::Cast(aPrincipal)->IsCodebasePrincipal())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@@ -1765,7 +1765,7 @@ public:
|
||||
|
||||
nsTArray<nsRefPtr<ServiceWorkerRegistrationMainThread>> array;
|
||||
|
||||
if (NS_WARN_IF(!BasePrincipal::IsCodebasePrincipal(principal))) {
|
||||
if (NS_WARN_IF(!BasePrincipal::Cast(principal)->IsCodebasePrincipal())) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -2796,7 +2796,7 @@ ServiceWorkerManager::PrincipalToScopeKey(nsIPrincipal* aPrincipal,
|
||||
{
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
|
||||
if (NS_WARN_IF(!BasePrincipal::IsCodebasePrincipal(aPrincipal))) {
|
||||
if (NS_WARN_IF(!BasePrincipal::Cast(aPrincipal)->IsCodebasePrincipal())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
+2
-18
@@ -838,21 +838,6 @@ URL::SearchParams()
|
||||
return mSearchParams;
|
||||
}
|
||||
|
||||
void
|
||||
URL::SetSearchParams(URLSearchParams& aSearchParams)
|
||||
{
|
||||
if (mSearchParams) {
|
||||
mSearchParams->RemoveObserver(this);
|
||||
}
|
||||
|
||||
mSearchParams = &aSearchParams;
|
||||
mSearchParams->AddObserver(this);
|
||||
|
||||
nsAutoString search;
|
||||
mSearchParams->Serialize(search);
|
||||
SetSearchInternal(search);
|
||||
}
|
||||
|
||||
void
|
||||
URL::GetHash(nsAString& aHash, ErrorResult& aRv) const
|
||||
{
|
||||
@@ -959,7 +944,7 @@ URL::UpdateURLSearchParams()
|
||||
nsAutoString search;
|
||||
ErrorResult rv;
|
||||
GetSearch(search, rv);
|
||||
mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(Substring(search, 1)), this);
|
||||
mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(Substring(search, 1)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -967,8 +952,7 @@ void
|
||||
URL::CreateSearchParamsIfNeeded()
|
||||
{
|
||||
if (!mSearchParams) {
|
||||
mSearchParams = new URLSearchParams();
|
||||
mSearchParams->AddObserver(this);
|
||||
mSearchParams = new URLSearchParams(this);
|
||||
UpdateURLSearchParams();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +108,6 @@ public:
|
||||
|
||||
URLSearchParams* SearchParams();
|
||||
|
||||
void SetSearchParams(URLSearchParams& aSearchParams);
|
||||
|
||||
void GetHash(nsAString& aHost, ErrorResult& aRv) const;
|
||||
|
||||
void SetHash(const nsAString& aHash, ErrorResult& aRv);
|
||||
|
||||
@@ -116,28 +116,6 @@ onmessage = function() {
|
||||
url.searchParams.set('e', 'f');
|
||||
ok(url.href.indexOf('e=f') != 1, 'URL right');
|
||||
|
||||
var u = new URLSearchParams();
|
||||
u.append('foo', 'bar');
|
||||
url.searchParams = u;
|
||||
is(url.searchParams, u, "URL.searchParams is the same object");
|
||||
is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')");
|
||||
is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
|
||||
|
||||
try {
|
||||
url.searchParams = null;
|
||||
ok(false, "URLSearchParams is not nullable");
|
||||
} catch(e) {
|
||||
ok(true, "URLSearchParams is not nullable");
|
||||
}
|
||||
|
||||
var url2 = new URL('http://www.example.net?e=f');
|
||||
url.searchParams = url2.searchParams;
|
||||
is(url.searchParams, url2.searchParams, "URL.searchParams is not the same object");
|
||||
is(url.searchParams.get('e'), 'f', "URL.searchParams.get('e')");
|
||||
|
||||
url.href = "http://www.example.net?bar=foo";
|
||||
is(url.searchParams.get('bar'), 'foo', "URL.searchParams.get('bar')");
|
||||
|
||||
runTest();
|
||||
}
|
||||
|
||||
@@ -148,11 +126,8 @@ onmessage = function() {
|
||||
[ '\u0541', '%D5%81'] ];
|
||||
|
||||
for (var i = 0; i < encoding.length; ++i) {
|
||||
var a = new URLSearchParams();
|
||||
a.set('a', encoding[i][0]);
|
||||
|
||||
var url = new URL('http://www.example.net');
|
||||
url.searchParams = a;
|
||||
url.searchParams.set('a', encoding[i][0]);
|
||||
is(url.href, 'http://www.example.net/?a=' + encoding[i][1]);
|
||||
|
||||
var url2 = new URL(url.href);
|
||||
@@ -162,39 +137,12 @@ onmessage = function() {
|
||||
runTest();
|
||||
}
|
||||
|
||||
function testMultiURL() {
|
||||
var a = new URL('http://www.example.net?a=b&c=d');
|
||||
var b = new URL('http://www.example.net?e=f');
|
||||
ok(a.searchParams.has('a'), "a.searchParams.has('a')");
|
||||
ok(a.searchParams.has('c'), "a.searchParams.has('c')");
|
||||
ok(b.searchParams.has('e'), "b.searchParams.has('e')");
|
||||
|
||||
var u = new URLSearchParams();
|
||||
a.searchParams = b.searchParams = u;
|
||||
is(a.searchParams, u, "a.searchParams === u");
|
||||
is(b.searchParams, u, "b.searchParams === u");
|
||||
ok(!a.searchParams.has('a'), "!a.searchParams.has('a')");
|
||||
ok(!a.searchParams.has('c'), "!a.searchParams.has('c')");
|
||||
ok(!b.searchParams.has('e'), "!b.searchParams.has('e')");
|
||||
|
||||
u.append('foo', 'bar');
|
||||
is(a.searchParams.get('foo'), 'bar', "a has foo=bar");
|
||||
is(b.searchParams.get('foo'), 'bar', "b has foo=bar");
|
||||
is(a + "", b + "", "stringify a == b");
|
||||
|
||||
a.search = "?bar=foo";
|
||||
is(a.searchParams.get('bar'), 'foo', "a has bar=foo");
|
||||
is(b.searchParams.get('bar'), 'foo', "b has bar=foo");
|
||||
|
||||
runTest();
|
||||
}
|
||||
var tests = [
|
||||
testSimpleURLSearchParams,
|
||||
testCopyURLSearchParams,
|
||||
testParserURLSearchParams,
|
||||
testURL,
|
||||
testEncoding,
|
||||
testMultiURL
|
||||
];
|
||||
|
||||
function runTest() {
|
||||
|
||||
@@ -99,15 +99,6 @@ static inline bool
|
||||
Enumerate(JSContext* cx, HandleObject pobj, jsid id,
|
||||
bool enumerable, unsigned flags, Maybe<IdSet>& ht, AutoIdVector* props)
|
||||
{
|
||||
// We implement __proto__ using a property on |Object.prototype|, but
|
||||
// because __proto__ is highly deserving of removal, we don't want it to
|
||||
// show up in property enumeration, even if only for |Object.prototype|
|
||||
// (think introspection by Prototype-like frameworks that add methods to
|
||||
// the built-in prototypes). So exclude __proto__ if the object where the
|
||||
// property was found has no [[Prototype]] and might be |Object.prototype|.
|
||||
if (MOZ_UNLIKELY(!pobj->getTaggedProto().isObject() && JSID_IS_ATOM(id, cx->names().proto)))
|
||||
return true;
|
||||
|
||||
if (!(flags & JSITER_OWNONLY) || pobj->is<ProxyObject>() || pobj->getOps()->enumerate) {
|
||||
if (!ht) {
|
||||
ht.emplace(cx);
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 690031;
|
||||
var summary =
|
||||
'Exclude __proto__ from showing up when enumerating properties of ' +
|
||||
'Object.prototype again';
|
||||
var BUGNUMBER = 837630;
|
||||
var summary ='__proto__ should show up with O.getOwnPropertyNames(O.prototype)';
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
@@ -16,8 +14,8 @@ print(BUGNUMBER + ": " + summary);
|
||||
**************/
|
||||
|
||||
var keys = Object.getOwnPropertyNames(Object.prototype);
|
||||
assertEq(keys.indexOf("__proto__"), -1,
|
||||
"shouldn't have gotten __proto__ as a property of Object.prototype " +
|
||||
assertEq(keys.indexOf("__proto__") >= 0, true,
|
||||
"should have gotten __proto__ as a property of Object.prototype " +
|
||||
"(got these properties: " + keys + ")");
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -324,18 +324,14 @@ FunctionForwarder(JSContext* cx, unsigned argc, Value* vp)
|
||||
RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0));
|
||||
RootedObject unwrappedFun(cx, js::UncheckedUnwrap(&v.toObject()));
|
||||
|
||||
RootedObject thisObj(cx, JS_THIS_OBJECT(cx, vp));
|
||||
if (!thisObj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject thisObj(cx, args.isConstructing() ? nullptr : JS_THIS_OBJECT(cx, vp));
|
||||
{
|
||||
// We manually implement the contents of CrossCompartmentWrapper::call
|
||||
// here, because certain function wrappers (notably content->nsEP) are
|
||||
// not callable.
|
||||
JSAutoCompartment ac(cx, unwrappedFun);
|
||||
|
||||
RootedValue thisVal(cx, ObjectValue(*thisObj));
|
||||
RootedValue thisVal(cx, ObjectOrNullValue(thisObj));
|
||||
if (!CheckSameOriginArg(cx, options, thisVal) || !JS_WrapObject(cx, &thisObj))
|
||||
return false;
|
||||
|
||||
@@ -345,8 +341,13 @@ FunctionForwarder(JSContext* cx, unsigned argc, Value* vp)
|
||||
}
|
||||
|
||||
RootedValue fval(cx, ObjectValue(*unwrappedFun));
|
||||
if (!JS_CallFunctionValue(cx, thisObj, fval, args, args.rval()))
|
||||
return false;
|
||||
if (args.isConstructing()) {
|
||||
if (!JS::Construct(cx, fval, args, args.rval()))
|
||||
return false;
|
||||
} else {
|
||||
if (!JS_CallFunctionValue(cx, thisObj, fval, args, args.rval()))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrap the return value into our compartment.
|
||||
@@ -361,8 +362,11 @@ NewFunctionForwarder(JSContext* cx, HandleId idArg, HandleObject callable,
|
||||
if (id == JSID_VOIDHANDLE)
|
||||
id = GetRTIdByIndex(cx, XPCJSRuntime::IDX_EMPTYSTRING);
|
||||
|
||||
JSFunction *fun = js::NewFunctionByIdWithReserved(cx, FunctionForwarder,
|
||||
0, 0, id);
|
||||
// We have no way of knowing whether the underlying function wants to be a
|
||||
// constructor or not, so we just mark all forwarders as constructors, and
|
||||
// let the underlying function throw for construct calls if it wants.
|
||||
JSFunction* fun = js::NewFunctionByIdWithReserved(cx, FunctionForwarder,
|
||||
0, JSFUN_CONSTRUCTOR, id);
|
||||
if (!fun)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -1053,7 +1053,11 @@ xpc::CreateSandboxObject(JSContext* cx, MutableHandleValue vp, nsISupports* prin
|
||||
return NS_ERROR_XPC_UNEXPECTED;
|
||||
|
||||
// Now check what sort of thing we've got in |proto|
|
||||
JSObject* unwrappedProto = js::UncheckedUnwrap(options.proto, false);
|
||||
JSObject* unwrappedProto = js::CheckedUnwrap(options.proto, false);
|
||||
if (!unwrappedProto) {
|
||||
JS_ReportError(cx, "Sandbox must subsume sandboxPrototype");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
const js::Class* unwrappedClass = js::GetObjectClass(unwrappedProto);
|
||||
if (IS_WN_CLASS(unwrappedClass) ||
|
||||
mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass))) {
|
||||
|
||||
@@ -160,11 +160,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
|
||||
"toLocaleDateString", "toLocaleTimeString", "toDateString", "toTimeString",
|
||||
"toISOString", "toJSON", "toSource", "toString", "valueOf", "constructor",
|
||||
"toGMTString"];
|
||||
gPrototypeProperties['Object'] = /* __proto__ is intentionally excluded here, because
|
||||
the JS engine filters it out of getOwnPropertyNames */
|
||||
gPrototypeProperties['Object'] =
|
||||
["constructor", "toSource", "toString", "toLocaleString", "valueOf", "watch",
|
||||
"unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
|
||||
"__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__"];
|
||||
"__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__",
|
||||
"__proto__"];
|
||||
gPrototypeProperties['Array'] =
|
||||
["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
|
||||
"pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
const Cu = Components.utils;
|
||||
|
||||
function testStrict(sb) {
|
||||
"use strict";
|
||||
do_check_eq(sb.eval("typeof wrappedCtor()"), "string");
|
||||
do_check_eq(sb.eval("typeof new wrappedCtor()"), "object");
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
var sb = new Cu.Sandbox(null);
|
||||
var dateCtor = sb.Date;
|
||||
sb.wrappedCtor = Cu.exportFunction(function wrapper(val) {
|
||||
"use strict";
|
||||
var constructing = this.constructor == wrapper;
|
||||
return constructing ? new dateCtor(val) : dateCtor(val);
|
||||
}, sb);
|
||||
do_check_eq(typeof Date(), "string");
|
||||
do_check_eq(typeof new Date(), "object");
|
||||
do_check_eq(sb.eval("typeof wrappedCtor()"), "string");
|
||||
do_check_eq(sb.eval("typeof new wrappedCtor()"), "object");
|
||||
testStrict(sb);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
function run_test() {
|
||||
let Cu = Components.utils;
|
||||
let sandbox1 = new Cu.Sandbox(null);
|
||||
let sandbox2 = new Cu.Sandbox(null);
|
||||
let arg = Cu.evalInSandbox('({ buf: ArrayBuffer(2) })', sandbox1);
|
||||
|
||||
let clonedArg = Cu.cloneInto(Cu.waiveXrays(arg), sandbox2);
|
||||
do_check_eq(typeof Cu.waiveXrays(clonedArg).buf, "object");
|
||||
|
||||
clonedArg = Cu.cloneInto(arg, sandbox2);
|
||||
do_check_eq(typeof Cu.waiveXrays(clonedArg).buf, "object");
|
||||
do_check_eq(Cu.waiveXrays(clonedArg).buf.constructor.name, "ArrayBuffer");
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
function run_test()
|
||||
{
|
||||
try {
|
||||
var sandbox = new Components.utils.Sandbox(null, {"sandboxPrototype" : {}});
|
||||
do_check_true(false);
|
||||
} catch (e) {
|
||||
do_check_true(/must subsume sandboxPrototype/.test(e));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
const Cu = Components.utils;
|
||||
function run_test() {
|
||||
do_check_throws_nsIException(() => Cu.getObjectPrincipal({}).equals(null), "NS_ERROR_ILLEGAL_VALUE");
|
||||
do_check_throws_nsIException(() => Cu.getObjectPrincipal({}).subsumes(null), "NS_ERROR_ILLEGAL_VALUE");
|
||||
}
|
||||
@@ -54,7 +54,11 @@ support-files =
|
||||
[test_bug1082450.js]
|
||||
[test_bug1081990.js]
|
||||
[test_bug1110546.js]
|
||||
[test_bug1131707.js]
|
||||
[test_bug1150106.js]
|
||||
[test_bug1150771.js]
|
||||
[test_bug1151385.js]
|
||||
[test_bug1170311.js]
|
||||
[test_bug_442086.js]
|
||||
[test_callFunctionWithAsyncStack.js]
|
||||
[test_file.js]
|
||||
|
||||
@@ -301,11 +301,29 @@ bool JSXrayTraits::getOwnPropertyFromTargetIfSafe(JSContext* cx,
|
||||
return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value not same-origin with target");
|
||||
}
|
||||
|
||||
// Disallow non-Xrayable objects.
|
||||
// Disallow (most) non-Xrayable objects.
|
||||
XrayType xrayType = GetXrayType(propObj);
|
||||
if (xrayType == NotXray || xrayType == XrayForOpaqueObject) {
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value not Xrayable");
|
||||
if (IdentifyStandardInstance(propObj) == JSProto_ArrayBuffer) {
|
||||
// Given that non-Xrayable objects are now opaque by default,
|
||||
// this restriction is somewhat more draconian than it needs to
|
||||
// be. It's true that script can't do much with an opaque
|
||||
// object, so in general it doesn't make much of a difference.
|
||||
// But one place it _does_ make a difference is in the
|
||||
// structured clone algorithm. When traversing an object to
|
||||
// clone it, the algorithm dutifully traverses inspects the
|
||||
// security wrapper without unwrapping it, so it won't see
|
||||
// properties we restrict here. But there are some object types
|
||||
// that the structured clone algorithm can handle safely even
|
||||
// without Xrays (i.e. ArrayBuffer, where it just clones the
|
||||
// underlying byte array).
|
||||
//
|
||||
// So we make some special cases here for such situations. Pass
|
||||
// them through.
|
||||
} else {
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value not Xrayable");
|
||||
}
|
||||
}
|
||||
|
||||
// Disallow callables.
|
||||
|
||||
@@ -30,7 +30,7 @@ interface URLUtils {
|
||||
attribute ScalarValueString port;
|
||||
attribute ScalarValueString pathname;
|
||||
attribute ScalarValueString search;
|
||||
attribute URLSearchParams searchParams;
|
||||
readonly attribute URLSearchParams searchParams;
|
||||
attribute ScalarValueString hash;
|
||||
};
|
||||
|
||||
|
||||
@@ -361,7 +361,8 @@ function _setupDebuggerServer(breakpointFiles, callback) {
|
||||
prefs.setBoolPref("devtools.debugger.log.verbose", true);
|
||||
}
|
||||
|
||||
let {DebuggerServer} = Components.utils.import('resource://gre/modules/devtools/dbg-server.jsm', {});
|
||||
let {DebuggerServer, OriginalLocation} =
|
||||
Components.utils.import('resource://gre/modules/devtools/dbg-server.jsm', {});
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
DebuggerServer.addActors("resource://testing-common/dbg-actors.js");
|
||||
@@ -383,7 +384,7 @@ function _setupDebuggerServer(breakpointFiles, callback) {
|
||||
let threadActor = subject.wrappedJSObject;
|
||||
for (let file of breakpointFiles) {
|
||||
let sourceActor = threadActor.sources.source({originalUrl: file});
|
||||
sourceActor.setBreakpoint(1);
|
||||
sourceActor._setBreakpoint(new OriginalLocation(sourceActor, 1));
|
||||
}
|
||||
} catch (ex) {
|
||||
do_print("Failed to initialize breakpoints: " + ex + "\n" + ex.stack);
|
||||
|
||||
@@ -145,7 +145,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
||||
URLSearchParams::URLSearchParams()
|
||||
URLSearchParams::URLSearchParams(URLSearchParamsObserver* aObserver)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -160,24 +160,11 @@ URLSearchParams::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
}
|
||||
|
||||
void
|
||||
URLSearchParams::ParseInput(const nsACString& aInput,
|
||||
URLSearchParamsObserver* aObserver)
|
||||
URLSearchParams::ParseInput(const nsACString& aInput)
|
||||
{
|
||||
NS_NOTREACHED("Unexpected call to URLSearchParams::ParseInput");
|
||||
}
|
||||
|
||||
void
|
||||
URLSearchParams::AddObserver(URLSearchParamsObserver* aObserver)
|
||||
{
|
||||
NS_NOTREACHED("Unexpected call to URLSearchParams::SetObserver");
|
||||
}
|
||||
|
||||
void
|
||||
URLSearchParams::RemoveObserver(URLSearchParamsObserver* aObserver)
|
||||
{
|
||||
NS_NOTREACHED("Unexpected call to URLSearchParams::SetObserver");
|
||||
}
|
||||
|
||||
void
|
||||
URLSearchParams::Serialize(nsAString& aValue) const
|
||||
{
|
||||
@@ -234,9 +221,9 @@ URLSearchParams::DeleteAll()
|
||||
}
|
||||
|
||||
void
|
||||
URLSearchParams::NotifyObservers(URLSearchParamsObserver* aExceptObserver)
|
||||
URLSearchParams::NotifyObserver()
|
||||
{
|
||||
NS_NOTREACHED("Unexpected call to URLSearchParams::NotifyObservers");
|
||||
NS_NOTREACHED("Unexpected call to URLSearchParams::NotifyObserver");
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -496,10 +496,22 @@ ThreadState.prototype = {
|
||||
// Ignore "interrupted" events, to avoid UI flicker. These are generated
|
||||
// by the slow script dialog and internal events such as setting
|
||||
// breakpoints. Pressing the resume button does need to be shown, though.
|
||||
if (aEvent == "paused" &&
|
||||
aPacket.why.type == "interrupted" &&
|
||||
!this.interruptedByResumeButton) {
|
||||
return;
|
||||
if (aEvent == "paused") {
|
||||
if (aPacket.why.type == "interrupted" &&
|
||||
!this.interruptedByResumeButton) {
|
||||
return;
|
||||
} else if (aPacket.why.type == "breakpointConditionThrown" && aPacket.why.message) {
|
||||
let where = aPacket.frame.where;
|
||||
let aLocation = {
|
||||
line: where.line,
|
||||
column: where.column,
|
||||
actor: where.source ? where.source.actor : null
|
||||
};
|
||||
DebuggerView.Sources.showBreakpointConditionThrownMessage(
|
||||
aLocation,
|
||||
aPacket.why.message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.interruptedByResumeButton = false;
|
||||
@@ -590,6 +602,10 @@ StackFrames.prototype = {
|
||||
case "breakpoint":
|
||||
this._currentBreakpointLocation = aPacket.frame.where;
|
||||
break;
|
||||
case "breakpointConditionThrown":
|
||||
this._currentBreakpointLocation = aPacket.frame.where;
|
||||
this._conditionThrowMessage = aPacket.why.message;
|
||||
break;
|
||||
// If paused by a client evaluation, store the evaluated value.
|
||||
case "clientEvaluated":
|
||||
this._currentEvaluation = aPacket.why.frameFinished;
|
||||
|
||||
@@ -468,6 +468,19 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
this._unselectBreakpoint();
|
||||
},
|
||||
|
||||
/**
|
||||
* Display the message thrown on breakpoint condition
|
||||
*/
|
||||
showBreakpointConditionThrownMessage: function(aLocation, aMessage = "") {
|
||||
let breakpointItem = this.getBreakpoint(aLocation);
|
||||
if (!breakpointItem) {
|
||||
return;
|
||||
}
|
||||
let attachment = breakpointItem.attachment;
|
||||
attachment.view.container.classList.add("dbg-breakpoint-condition-thrown");
|
||||
attachment.view.message.setAttribute("value", aMessage);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the checked/unchecked and enabled/disabled states of the buttons in
|
||||
* the sources toolbar based on the currently selected source's state.
|
||||
@@ -707,12 +720,13 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
* - location: the breakpoint's source location and line number
|
||||
* - disabled: the breakpoint's disabled state, boolean
|
||||
* - text: the breakpoint's line text to be displayed
|
||||
* - message: thrown string when the breakpoint condition throws,
|
||||
* @return object
|
||||
* An object containing the breakpoint container, checkbox,
|
||||
* line number and line text nodes.
|
||||
*/
|
||||
_createBreakpointView: function(aOptions) {
|
||||
let { location, disabled, text } = aOptions;
|
||||
let { location, disabled, text, message } = aOptions;
|
||||
let identifier = DebuggerController.Breakpoints.getIdentifier(location);
|
||||
|
||||
let checkbox = document.createElement("checkbox");
|
||||
@@ -729,9 +743,29 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
lineTextNode.setAttribute("crop", "end");
|
||||
lineTextNode.setAttribute("flex", "1");
|
||||
|
||||
let tooltip = text.substr(0, BREAKPOINT_LINE_TOOLTIP_MAX_LENGTH);
|
||||
let tooltip = text ? text.substr(0, BREAKPOINT_LINE_TOOLTIP_MAX_LENGTH) : "";
|
||||
lineTextNode.setAttribute("tooltiptext", tooltip);
|
||||
|
||||
let thrownNode = document.createElement("label");
|
||||
thrownNode.className = "plain dbg-breakpoint-condition-thrown-message dbg-breakpoint-text";
|
||||
thrownNode.setAttribute("value", message);
|
||||
thrownNode.setAttribute("crop", "end");
|
||||
thrownNode.setAttribute("flex", "1");
|
||||
|
||||
let bpLineContainer = document.createElement("hbox");
|
||||
bpLineContainer.className = "plain dbg-breakpoint-line-container";
|
||||
bpLineContainer.setAttribute("flex", "1");
|
||||
|
||||
bpLineContainer.appendChild(lineNumberNode);
|
||||
bpLineContainer.appendChild(lineTextNode);
|
||||
|
||||
let bpDetailContainer = document.createElement("vbox");
|
||||
bpDetailContainer.className = "plain dbg-breakpoint-detail-container";
|
||||
bpDetailContainer.setAttribute("flex", "1");
|
||||
|
||||
bpDetailContainer.appendChild(bpLineContainer);
|
||||
bpDetailContainer.appendChild(thrownNode);
|
||||
|
||||
let container = document.createElement("hbox");
|
||||
container.id = "breakpoint-" + identifier;
|
||||
container.className = "dbg-breakpoint side-menu-widget-item-other";
|
||||
@@ -743,14 +777,14 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
checkbox.addEventListener("click", this._onBreakpointCheckboxClick, false);
|
||||
|
||||
container.appendChild(checkbox);
|
||||
container.appendChild(lineNumberNode);
|
||||
container.appendChild(lineTextNode);
|
||||
container.appendChild(bpDetailContainer);
|
||||
|
||||
return {
|
||||
container: container,
|
||||
checkbox: checkbox,
|
||||
lineNumber: lineNumberNode,
|
||||
lineText: lineTextNode
|
||||
lineText: lineTextNode,
|
||||
message: thrownNode
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1010,7 +1044,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
let attachment = breakpointItem.attachment;
|
||||
|
||||
// Check if this is an enabled conditional breakpoint, and if so,
|
||||
// save the current conditional epression.
|
||||
// save the current conditional expression.
|
||||
let breakpointPromise = DebuggerController.Breakpoints._getAdded(attachment);
|
||||
if (breakpointPromise) {
|
||||
let { location } = yield breakpointPromise;
|
||||
|
||||
@@ -59,6 +59,7 @@ support-files =
|
||||
doc_event-listeners-01.html
|
||||
doc_event-listeners-02.html
|
||||
doc_event-listeners-03.html
|
||||
doc_event-listeners-04.html
|
||||
doc_frame-parameters.html
|
||||
doc_function-display-name.html
|
||||
doc_function-search.html
|
||||
@@ -103,71 +104,49 @@ support-files =
|
||||
[browser_dbg_aaa_run_first_leaktest.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_addonactor.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_addon-sources.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_addon-modules.js]
|
||||
skip-if = e10s # TODO
|
||||
[browser_dbg_addon-modules-unpacked.js]
|
||||
skip-if = e10s # TODO
|
||||
[browser_dbg_addon-panels.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_addon-console.js]
|
||||
skip-if = e10s && debug || os == 'win' # bug 1005274
|
||||
[browser_dbg_auto-pretty-print-01.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_auto-pretty-print-02.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_bfcache.js]
|
||||
skip-if = e10s || true # bug 1113935
|
||||
[browser_dbg_blackboxing-01.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_blackboxing-02.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_blackboxing-03.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_blackboxing-04.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_blackboxing-05.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_blackboxing-06.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_breadcrumbs-access.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_break-on-dom-01.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_break-on-dom-02.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_break-on-dom-03.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_break-on-dom-04.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_break-on-dom-05.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_break-on-dom-06.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_break-on-dom-07.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_break-on-dom-08.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_break-on-dom-event-01.js]
|
||||
skip-if = e10s || os == "mac" || e10s # Bug 895426
|
||||
[browser_dbg_break-on-dom-event-02.js]
|
||||
skip-if = e10s # TODO
|
||||
[browser_dbg_break-on-dom-event-03.js]
|
||||
skip-if = e10s # TODO
|
||||
[browser_dbg_breakpoints-actual-location.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_breakpoints-actual-location2.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js]
|
||||
skip-if = e10s # Bug 1093535
|
||||
[browser_dbg_breakpoints-button-01.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_breakpoints-button-02.js]
|
||||
[browser_dbg_breakpoints-condition-thrown-message.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_breakpoints-contextmenu-add.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_breakpoints-contextmenu.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_breakpoints-disabled-reload.js]
|
||||
skip-if = e10s # Bug 1093535
|
||||
[browser_dbg_breakpoints-editor.js]
|
||||
@@ -208,6 +187,8 @@ skip-if = e10s && debug
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_conditional-breakpoints-04.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_conditional-breakpoints-05.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_server-conditional-bp-01.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_server-conditional-bp-02.js]
|
||||
@@ -216,6 +197,8 @@ skip-if = e10s && debug
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_server-conditional-bp-04.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_server-conditional-bp-05.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_controller-evaluate-01.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_controller-evaluate-02.js]
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that the break-on-dom-events request works for load event listeners.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_event-listeners-04.html";
|
||||
|
||||
let gClient, gThreadClient;
|
||||
|
||||
function test() {
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
}
|
||||
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
gClient = new DebuggerClient(transport);
|
||||
gClient.connect((aType, aTraits) => {
|
||||
is(aType, "browser",
|
||||
"Root actor should identify itself as a browser.");
|
||||
|
||||
addTab(TAB_URL)
|
||||
.then(() => attachThreadActorForUrl(gClient, TAB_URL))
|
||||
.then(aThreadClient => gThreadClient = aThreadClient)
|
||||
.then(pauseDebuggee)
|
||||
.then(testBreakOnLoad)
|
||||
.then(closeConnection)
|
||||
.then(finish)
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function pauseDebuggee() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
gClient.addOneTimeListener("paused", (aEvent, aPacket) => {
|
||||
is(aPacket.type, "paused",
|
||||
"We should now be paused.");
|
||||
is(aPacket.why.type, "debuggerStatement",
|
||||
"The debugger statement was hit.");
|
||||
|
||||
gThreadClient.resume(deferred.resolve);
|
||||
});
|
||||
|
||||
// Spin the event loop before causing the debuggee to pause, to allow
|
||||
// this function to return first.
|
||||
executeSoon(() => triggerButtonClick());
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Test pause on a load event.
|
||||
function testBreakOnLoad() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
// Test calling pauseOnDOMEvents from a running state.
|
||||
gThreadClient.pauseOnDOMEvents(["load"], (aPacket) => {
|
||||
is(aPacket.error, undefined,
|
||||
"The pause-on-load request completed successfully.");
|
||||
let handlers = ["loadHandler"];
|
||||
|
||||
gClient.addListener("paused", function tester(aEvent, aPacket) {
|
||||
is(aPacket.why.type, "pauseOnDOMEvents",
|
||||
"A hidden breakpoint was hit.");
|
||||
|
||||
is(aPacket.frame.where.line, 15, "Found the load event listener.");
|
||||
gClient.removeListener("paused", tester);
|
||||
deferred.resolve();
|
||||
|
||||
gThreadClient.resume(() => triggerButtonClick(handlers.slice(-1)));
|
||||
});
|
||||
|
||||
getTabActorForUrl(gClient, TAB_URL).then(aGrip => {
|
||||
gClient.attachTab(aGrip.actor, (aResponse, aTabClient) => {
|
||||
aTabClient.reload();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function triggerButtonClick() {
|
||||
let button = content.document.querySelector("button");
|
||||
EventUtils.sendMouseEvent({ type: "click" }, button);
|
||||
}
|
||||
|
||||
function closeConnection() {
|
||||
let deferred = promise.defer();
|
||||
gClient.close(deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gClient = null;
|
||||
gThreadClient = null;
|
||||
});
|
||||
@@ -0,0 +1,106 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Make sure that the message which breakpoint condition throws
|
||||
* could be displayed on UI correctly
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html";
|
||||
|
||||
function test() {
|
||||
let gTab, gPanel, gDebugger, gEditor;
|
||||
let gSources, gLocation;
|
||||
|
||||
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gEditor = gDebugger.DebuggerView.editor;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, ".html", 17)
|
||||
.then(addBreakpoints)
|
||||
.then(() => resumeAndTestThrownMessage(18))
|
||||
.then(() => resumeAndTestNoThrownMessage(19))
|
||||
.then(() => resumeAndTestThrownMessage(22))
|
||||
.then(() => resumeAndFinishTest())
|
||||
.then(() => closeDebuggerAndFinish(gPanel))
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
|
||||
callInTab(gTab, "ermahgerd");
|
||||
});
|
||||
|
||||
function resumeAndTestThrownMessage(aLine) {
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
gDebugger.document.getElementById("resume"),
|
||||
gDebugger);
|
||||
|
||||
let finished = waitForCaretUpdated(gPanel, aLine).then(() => {
|
||||
//test that the thrown message is correctly shown
|
||||
let attachment = gSources.getBreakpoint({ actor: gSources.values[0], line: aLine}).attachment;
|
||||
ok(attachment.view.container.classList.contains('dbg-breakpoint-condition-thrown'),
|
||||
"Message on line " + aLine + " should be shown when condition throws.");
|
||||
});
|
||||
|
||||
return finished;
|
||||
}
|
||||
|
||||
function resumeAndTestNoThrownMessage(aLine) {
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
gDebugger.document.getElementById("resume"),
|
||||
gDebugger);
|
||||
|
||||
let finished = waitForCaretUpdated(gPanel, aLine).then(() => {
|
||||
//test that the thrown message is correctly shown
|
||||
let attachment = gSources.getBreakpoint({ actor: gSources.values[0], line: aLine}).attachment;
|
||||
ok(!attachment.view.container.classList.contains("dbg-breakpoint-condition-thrown"),
|
||||
"Message on line " + aLine + " should be hidden if condition doesn't throw.");
|
||||
});
|
||||
return finished;
|
||||
}
|
||||
|
||||
function resumeAndFinishTest() {
|
||||
let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.AFTER_FRAMES_CLEARED)
|
||||
|
||||
gDebugger.gThreadClient.resume();
|
||||
|
||||
return finished;
|
||||
}
|
||||
|
||||
function addBreakpoints() {
|
||||
return promise.resolve(null)
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue,
|
||||
line: 18,
|
||||
condition: " 1a"}))
|
||||
.then(() => initialCheck(18))
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue,
|
||||
line: 19,
|
||||
condition: "true"}))
|
||||
.then(() => initialCheck(19))
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue,
|
||||
line: 20,
|
||||
condition: "false"}))
|
||||
.then(() => initialCheck(20))
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue,
|
||||
line: 22,
|
||||
condition: "randomVar"}))
|
||||
.then(() => initialCheck(22));
|
||||
}
|
||||
|
||||
function initialCheck(aCaretLine) {
|
||||
let bp = gSources.getBreakpoint({ actor: gSources.values[0], line: aCaretLine})
|
||||
let attachment = bp.attachment;
|
||||
ok(attachment,
|
||||
"There should be an item for line " + aCaretLine + " in the sources pane.");
|
||||
|
||||
let thrownNode = attachment.view.container.querySelector(".dbg-breakpoint-condition-thrown-message");
|
||||
ok(thrownNode,
|
||||
"The breakpoint item should contain a thrown message node.")
|
||||
|
||||
ok(!attachment.view.container.classList.contains("dbg-breakpoint-condition-thrown"),
|
||||
"The thrown message on line " + aCaretLine + " should be hidden when condition has not been evaluated.")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Make sure that conditional breakpoints with an exception-throwing expression
|
||||
* could pause on hit
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html";
|
||||
|
||||
function test() {
|
||||
let gTab, gPanel, gDebugger, gEditor;
|
||||
let gSources, gBreakpoints, gLocation;
|
||||
|
||||
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gEditor = gDebugger.DebuggerView.editor;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
gBreakpoints = gDebugger.DebuggerController.Breakpoints;
|
||||
|
||||
// This test forces conditional breakpoints to be evaluated on the
|
||||
// client-side
|
||||
var client = gPanel.target.client;
|
||||
client.mainRoot.traits.conditionalBreakpoints = false;
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, ".html", 17)
|
||||
.then(addBreakpoints)
|
||||
.then(() => resumeAndTestBreakpoint(18))
|
||||
.then(() => resumeAndTestBreakpoint(19))
|
||||
.then(() => resumeAndTestBreakpoint(20))
|
||||
.then(() => resumeAndTestBreakpoint(23))
|
||||
.then(() => resumeAndTestNoBreakpoint())
|
||||
.then(() => {
|
||||
// Reset traits back to default value
|
||||
client.mainRoot.traits.conditionalBreakpoints = true;
|
||||
})
|
||||
.then(() => closeDebuggerAndFinish(gPanel))
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
|
||||
callInTab(gTab, "ermahgerd");
|
||||
});
|
||||
|
||||
function resumeAndTestBreakpoint(aLine) {
|
||||
let finished = waitForCaretUpdated(gPanel, aLine).then(() => testBreakpoint(aLine));
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
gDebugger.document.getElementById("resume"),
|
||||
gDebugger);
|
||||
|
||||
return finished;
|
||||
}
|
||||
|
||||
function resumeAndTestNoBreakpoint() {
|
||||
let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.AFTER_FRAMES_CLEARED).then(() => {
|
||||
is(gSources.itemCount, 1,
|
||||
"Found the expected number of sources.");
|
||||
is(gEditor.getText().indexOf("ermahgerd"), 253,
|
||||
"The correct source was loaded initially.");
|
||||
is(gSources.selectedValue, gSources.values[0],
|
||||
"The correct source is selected.");
|
||||
|
||||
ok(gSources.selectedItem,
|
||||
"There should be a selected source in the sources pane.");
|
||||
ok(!gSources._selectedBreakpointItem,
|
||||
"There should be no selected breakpoint in the sources pane.");
|
||||
is(gSources._conditionalPopupVisible, false,
|
||||
"The breakpoint conditional expression popup should not be shown.");
|
||||
|
||||
is(gDebugger.document.querySelectorAll(".dbg-stackframe").length, 0,
|
||||
"There should be no visible stackframes.");
|
||||
is(gDebugger.document.querySelectorAll(".dbg-breakpoint").length, 6,
|
||||
"There should be thirteen visible breakpoints.");
|
||||
});
|
||||
|
||||
gDebugger.gThreadClient.resume();
|
||||
|
||||
return finished;
|
||||
}
|
||||
|
||||
function addBreakpoints() {
|
||||
return promise.resolve(null)
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue, line: 18 }))
|
||||
.then(aClient => aClient.conditionalExpression = " 1a")
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue, line: 19 }))
|
||||
.then(aClient => aClient.conditionalExpression = "new Error()")
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue, line: 20 }))
|
||||
.then(aClient => aClient.conditionalExpression = "true")
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue, line: 21 }))
|
||||
.then(aClient => aClient.conditionalExpression = "false")
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue, line: 22 }))
|
||||
.then(aClient => aClient.conditionalExpression = "0")
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue, line: 23 }))
|
||||
.then(aClient => aClient.conditionalExpression = "randomVar");
|
||||
}
|
||||
|
||||
function testBreakpoint(aLine, aHighlightBreakpoint) {
|
||||
// Highlight the breakpoint only if required.
|
||||
if (aHighlightBreakpoint) {
|
||||
let finished = waitForCaretUpdated(gPanel, aLine).then(() => testBreakpoint(aLine));
|
||||
gSources.highlightBreakpoint({ actor: gSources.selectedValue, line: aLine });
|
||||
return finished;
|
||||
}
|
||||
|
||||
let selectedActor = gSources.selectedValue;
|
||||
let selectedBreakpoint = gSources._selectedBreakpointItem;
|
||||
|
||||
ok(selectedActor,
|
||||
"There should be a selected item in the sources pane.");
|
||||
ok(selectedBreakpoint,
|
||||
"There should be a selected breakpoint in the sources pane.");
|
||||
|
||||
let source = gSources.selectedItem.attachment.source;
|
||||
|
||||
is(selectedBreakpoint.attachment.actor, source.actor,
|
||||
"The breakpoint on line " + aLine + " wasn't added on the correct source.");
|
||||
is(selectedBreakpoint.attachment.line, aLine,
|
||||
"The breakpoint on line " + aLine + " wasn't found.");
|
||||
is(!!selectedBreakpoint.attachment.disabled, false,
|
||||
"The breakpoint on line " + aLine + " should be enabled.");
|
||||
is(!!selectedBreakpoint.attachment.openPopup, false,
|
||||
"The breakpoint on line " + aLine + " should not have opened a popup.");
|
||||
is(gSources._conditionalPopupVisible, false,
|
||||
"The breakpoint conditional expression popup should not have been shown.");
|
||||
|
||||
return gBreakpoints._getAdded(selectedBreakpoint.attachment).then(aBreakpointClient => {
|
||||
is(aBreakpointClient.location.url, source.url,
|
||||
"The breakpoint's client url is correct");
|
||||
is(aBreakpointClient.location.line, aLine,
|
||||
"The breakpoint's client line is correct");
|
||||
isnot(aBreakpointClient.conditionalExpression, undefined,
|
||||
"The breakpoint on line " + aLine + " should have a conditional expression.");
|
||||
|
||||
ok(isCaretPos(gPanel, aLine),
|
||||
"The editor caret position is not properly set.");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ function test() {
|
||||
.then(() => resumeAndTestBreakpoint(27))
|
||||
.then(() => resumeAndTestBreakpoint(28))
|
||||
.then(() => resumeAndTestBreakpoint(29))
|
||||
.then(() => resumeAndTestBreakpoint(30))
|
||||
.then(() => resumeAndTestNoBreakpoint())
|
||||
.then(() => {
|
||||
return promise.all([
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test conditional breakpoints throwing exceptions
|
||||
* with server support
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html";
|
||||
|
||||
function test() {
|
||||
// Linux debug test slaves are a bit slow at this test sometimes.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
let gTab, gPanel, gDebugger;
|
||||
let gEditor, gSources, gBreakpoints, gBreakpointsAdded, gBreakpointsRemoving;
|
||||
|
||||
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gEditor = gDebugger.DebuggerView.editor;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
gBreakpoints = gDebugger.DebuggerController.Breakpoints;
|
||||
gBreakpointsAdded = gBreakpoints._added;
|
||||
gBreakpointsRemoving = gBreakpoints._removing;
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, ".html", 17)
|
||||
.then(() => addBreakpoints())
|
||||
.then(() => initialChecks())
|
||||
.then(() => resumeAndTestBreakpoint(18))
|
||||
.then(() => resumeAndTestBreakpoint(19))
|
||||
.then(() => resumeAndTestBreakpoint(20))
|
||||
.then(() => resumeAndTestBreakpoint(23))
|
||||
.then(() => resumeAndTestNoBreakpoint())
|
||||
.then(() => closeDebuggerAndFinish(gPanel))
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
|
||||
callInTab(gTab, "ermahgerd");
|
||||
});
|
||||
|
||||
function addBreakpoints() {
|
||||
return promise.resolve(null)
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue,
|
||||
line: 18,
|
||||
condition: "1a"
|
||||
}))
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue,
|
||||
line: 19,
|
||||
condition: "new Error()"
|
||||
}))
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue,
|
||||
line: 20,
|
||||
condition: "true"
|
||||
}))
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue,
|
||||
line: 21,
|
||||
condition: "false"
|
||||
}))
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue,
|
||||
line: 22,
|
||||
condition: "undefined"
|
||||
}))
|
||||
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue,
|
||||
line: 23,
|
||||
condition: "randomVar"
|
||||
}));
|
||||
}
|
||||
|
||||
function initialChecks() {
|
||||
is(gDebugger.gThreadClient.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
is(gSources.itemCount, 1,
|
||||
"Found the expected number of sources.");
|
||||
is(gEditor.getText().indexOf("ermahgerd"), 253,
|
||||
"The correct source was loaded initially.");
|
||||
is(gSources.selectedValue, gSources.values[0],
|
||||
"The correct source is selected.");
|
||||
|
||||
is(gBreakpointsAdded.size, 6,
|
||||
"6 breakpoints currently added.");
|
||||
is(gBreakpointsRemoving.size, 0,
|
||||
"No breakpoints currently being removed.");
|
||||
is(gEditor.getBreakpoints().length, 6,
|
||||
"6 breakpoints currently shown in the editor.");
|
||||
|
||||
ok(!gBreakpoints._getAdded({ url: "foo", line: 3 }),
|
||||
"_getAdded('foo', 3) returns falsey.");
|
||||
ok(!gBreakpoints._getRemoving({ url: "bar", line: 3 }),
|
||||
"_getRemoving('bar', 3) returns falsey.");
|
||||
}
|
||||
|
||||
function resumeAndTestBreakpoint(aLine) {
|
||||
let finished = waitForCaretUpdated(gPanel, aLine).then(() => testBreakpoint(aLine));
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
gDebugger.document.getElementById("resume"),
|
||||
gDebugger);
|
||||
|
||||
return finished;
|
||||
}
|
||||
|
||||
function resumeAndTestNoBreakpoint() {
|
||||
let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.AFTER_FRAMES_CLEARED).then(() => {
|
||||
is(gSources.itemCount, 1,
|
||||
"Found the expected number of sources.");
|
||||
is(gEditor.getText().indexOf("ermahgerd"), 253,
|
||||
"The correct source was loaded initially.");
|
||||
is(gSources.selectedValue, gSources.values[0],
|
||||
"The correct source is selected.");
|
||||
|
||||
ok(gSources.selectedItem,
|
||||
"There should be a selected source in the sources pane.")
|
||||
ok(!gSources._selectedBreakpointItem,
|
||||
"There should be no selected breakpoint in the sources pane.")
|
||||
is(gSources._conditionalPopupVisible, false,
|
||||
"The breakpoint conditional expression popup should not be shown.");
|
||||
|
||||
is(gDebugger.document.querySelectorAll(".dbg-stackframe").length, 0,
|
||||
"There should be no visible stackframes.");
|
||||
is(gDebugger.document.querySelectorAll(".dbg-breakpoint").length, 6,
|
||||
"There should be thirteen visible breakpoints.");
|
||||
});
|
||||
|
||||
gDebugger.gThreadClient.resume();
|
||||
|
||||
return finished;
|
||||
}
|
||||
|
||||
function testBreakpoint(aLine, aHighlightBreakpoint) {
|
||||
// Highlight the breakpoint only if required.
|
||||
if (aHighlightBreakpoint) {
|
||||
let finished = waitForCaretUpdated(gPanel, aLine).then(() => testBreakpoint(aLine));
|
||||
gSources.highlightBreakpoint({ actor: gSources.selectedValue, line: aLine });
|
||||
return finished;
|
||||
}
|
||||
|
||||
let selectedActor = gSources.selectedValue;
|
||||
let selectedBreakpoint = gSources._selectedBreakpointItem;
|
||||
|
||||
ok(selectedActor,
|
||||
"There should be a selected item in the sources pane.");
|
||||
ok(selectedBreakpoint,
|
||||
"There should be a selected brekapoint in the sources pane.");
|
||||
|
||||
is(selectedBreakpoint.attachment.actor, selectedActor,
|
||||
"The breakpoint on line " + aLine + " wasn't added on the correct source.");
|
||||
is(selectedBreakpoint.attachment.line, aLine,
|
||||
"The breakpoint on line " + aLine + " wasn't found.");
|
||||
is(!!selectedBreakpoint.attachment.disabled, false,
|
||||
"The breakpoint on line " + aLine + " should be enabled.");
|
||||
is(!!selectedBreakpoint.attachment.openPopup, false,
|
||||
"The breakpoint on line " + aLine + " should not have opened a popup.");
|
||||
is(gSources._conditionalPopupVisible, false,
|
||||
"The breakpoint conditional expression popup should not have been shown.");
|
||||
|
||||
return gBreakpoints._getAdded(selectedBreakpoint.attachment).then(aBreakpointClient => {
|
||||
is(aBreakpointClient.location.actor, selectedActor,
|
||||
"The breakpoint's client url is correct");
|
||||
is(aBreakpointClient.location.line, aLine,
|
||||
"The breakpoint's client line is correct");
|
||||
isnot(aBreakpointClient.condition, undefined,
|
||||
"The breakpoint on line " + aLine + " should have a conditional expression.");
|
||||
|
||||
ok(isCaretPos(gPanel, aLine),
|
||||
"The editor caret position is not properly set.");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -79,15 +79,15 @@ function testSetBreakpointBlankLine() {
|
||||
let sourceForm = getSourceForm(gSources, COFFEE_URL);
|
||||
|
||||
let source = gDebugger.gThreadClient.source(sourceForm);
|
||||
source.setBreakpoint({ line: 3 }, aResponse => {
|
||||
source.setBreakpoint({ line: 7 }, aResponse => {
|
||||
ok(!aResponse.error,
|
||||
"Should be able to set a breakpoint in a coffee source file on a blank line.");
|
||||
ok(aResponse.actualLocation,
|
||||
"Because 3 is empty, we should have an actualLocation.");
|
||||
is(aResponse.actualLocation.source.url, COFFEE_URL,
|
||||
"actualLocation.actor should be source mapped to the coffee file.");
|
||||
is(aResponse.actualLocation.line, 2,
|
||||
"actualLocation.line should be source mapped back to 2.");
|
||||
is(aResponse.actualLocation.line, 8,
|
||||
"actualLocation.line should be source mapped back to 8.");
|
||||
|
||||
deferred.resolve();
|
||||
});
|
||||
@@ -147,7 +147,7 @@ function testStepping() {
|
||||
is(aPacket.frame.environment.bindings.variables.start.value, 0,
|
||||
"'start' is 0.");
|
||||
is(aPacket.frame.environment.bindings.variables.stop.value, 5,
|
||||
"'stop' hasn't been assigned to yet.");
|
||||
"'stop' is 5.");
|
||||
is(aPacket.frame.environment.bindings.variables.pivot.value.type, "undefined",
|
||||
"'pivot' hasn't been assigned to yet.");
|
||||
|
||||
|
||||
@@ -45,11 +45,11 @@ function testSetBreakpoint() {
|
||||
let sourceForm = getSourceForm(gSources, JS_URL);
|
||||
let source = gDebugger.gThreadClient.source(sourceForm);
|
||||
|
||||
source.setBreakpoint({ line: 30, column: 21 }, aResponse => {
|
||||
source.setBreakpoint({ line: 30 }, aResponse => {
|
||||
ok(!aResponse.error,
|
||||
"Should be able to set a breakpoint in a js file.");
|
||||
ok(!aResponse.actualLocation,
|
||||
"Should be able to set a breakpoint on line 30 and column 10.");
|
||||
"Should be able to set a breakpoint on line 30.");
|
||||
|
||||
gDebugger.gClient.addOneTimeListener("resumed", () => {
|
||||
waitForCaretAndScopes(gPanel, 30).then(() => {
|
||||
|
||||
@@ -89,11 +89,11 @@ function testSetBreakpoint() {
|
||||
let sourceForm = getSourceForm(gSources, JS_URL);
|
||||
let source = gDebugger.gThreadClient.source(sourceForm);
|
||||
|
||||
source.setBreakpoint({ line: 3, column: 61 }, aResponse => {
|
||||
source.setBreakpoint({ line: 3, column: 18 }, aResponse => {
|
||||
ok(!aResponse.error,
|
||||
"Should be able to set a breakpoint in a js file.");
|
||||
ok(!aResponse.actualLocation,
|
||||
"Should be able to set a breakpoint on line 3 and column 61.");
|
||||
"Should be able to set a breakpoint on line 3 and column 18.");
|
||||
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Debugger test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<button>Click me!</button>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.addEventListener("load", function onload() {
|
||||
var button = document.querySelector("button");
|
||||
button.onclick = function () {
|
||||
debugger;
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -295,6 +295,146 @@ ActorPool.prototype = {
|
||||
|
||||
exports.ActorPool = ActorPool;
|
||||
|
||||
/**
|
||||
* An OriginalLocation represents a location in an original source.
|
||||
*
|
||||
* @param SourceActor actor
|
||||
* A SourceActor representing an original source.
|
||||
* @param Number line
|
||||
* A line within the given source.
|
||||
* @param Number column
|
||||
* A column within the given line.
|
||||
* @param String name
|
||||
* The name of the symbol corresponding to this OriginalLocation.
|
||||
*/
|
||||
function OriginalLocation(actor, line, column, name) {
|
||||
this._connection = actor ? actor.conn : null;
|
||||
this._actorID = actor ? actor.actorID : undefined;
|
||||
this._line = line;
|
||||
this._column = column;
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
OriginalLocation.fromGeneratedLocation = function (generatedLocation) {
|
||||
return new OriginalLocation(
|
||||
generatedLocation.generatedSourceActor,
|
||||
generatedLocation.generatedLine,
|
||||
generatedLocation.generatedColumn
|
||||
);
|
||||
};
|
||||
|
||||
OriginalLocation.prototype = {
|
||||
get originalSourceActor() {
|
||||
return this._connection ? this._connection.getActor(this._actorID) : null;
|
||||
},
|
||||
|
||||
get originalUrl() {
|
||||
let actor = this.originalSourceActor;
|
||||
let source = actor.source;
|
||||
return source ? source.url : actor._originalUrl;
|
||||
},
|
||||
|
||||
get originalLine() {
|
||||
return this._line;
|
||||
},
|
||||
|
||||
get originalColumn() {
|
||||
return this._column;
|
||||
},
|
||||
|
||||
get originalName() {
|
||||
return this._name;
|
||||
},
|
||||
|
||||
get generatedSourceActor() {
|
||||
throw new Error("Shouldn't access generatedSourceActor from an OriginalLocation");
|
||||
},
|
||||
|
||||
get generatedLine() {
|
||||
throw new Error("Shouldn't access generatedLine from an OriginalLocation");
|
||||
},
|
||||
|
||||
get generatedColumn() {
|
||||
throw new Error("Shouldn't access generatedColumn from an Originallocation");
|
||||
},
|
||||
|
||||
equals: function (other) {
|
||||
return this.originalSourceActor.url == other.originalSourceActor.url &&
|
||||
this.originalLine === other.originalLine;
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
return {
|
||||
source: this.originalSourceActor.form(),
|
||||
line: this.originalLine,
|
||||
column: this.originalColumn
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
exports.OriginalLocation = OriginalLocation;
|
||||
|
||||
/**
|
||||
* A GeneratedLocation represents a location in an original source.
|
||||
*
|
||||
* @param SourceActor actor
|
||||
* A SourceActor representing a generated source.
|
||||
* @param Number line
|
||||
* A line within the given source.
|
||||
* @param Number column
|
||||
* A column within the given line.
|
||||
*/
|
||||
function GeneratedLocation(actor, line, column) {
|
||||
this._connection = actor ? actor.conn : null;
|
||||
this._actorID = actor ? actor.actorID : undefined;
|
||||
this._line = line;
|
||||
this._column = column;
|
||||
}
|
||||
|
||||
GeneratedLocation.fromOriginalLocation = function (originalLocation) {
|
||||
return new GeneratedLocation(
|
||||
originalLocation.originalSourceActor,
|
||||
originalLocation.originalLine,
|
||||
originalLocation.originalColumn
|
||||
);
|
||||
};
|
||||
|
||||
GeneratedLocation.prototype = {
|
||||
get originalSourceActor() {
|
||||
throw new Error();
|
||||
},
|
||||
|
||||
get originalUrl() {
|
||||
throw new Error("Shouldn't access originalUrl from a GeneratedLocation");
|
||||
},
|
||||
|
||||
get originalLine() {
|
||||
throw new Error("Shouldn't access originalLine from a GeneratedLocation");
|
||||
},
|
||||
|
||||
get originalColumn() {
|
||||
throw new Error("Shouldn't access originalColumn from a GeneratedLocation");
|
||||
},
|
||||
|
||||
get originalName() {
|
||||
throw new Error("Shouldn't access originalName from a GeneratedLocation");
|
||||
},
|
||||
|
||||
get generatedSourceActor() {
|
||||
return this._connection ? this._connection.getActor(this._actorID) : null;
|
||||
},
|
||||
|
||||
get generatedLine() {
|
||||
return this._line;
|
||||
},
|
||||
|
||||
get generatedColumn() {
|
||||
return this._column;
|
||||
}
|
||||
};
|
||||
|
||||
exports.GeneratedLocation = GeneratedLocation;
|
||||
|
||||
// TODO bug 863089: use Debugger.Script.prototype.getOffsetColumn when it is
|
||||
// implemented.
|
||||
exports.getOffsetColumn = function getOffsetColumn(aOffset, aScript) {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const protocol = require("devtools/server/protocol");
|
||||
const { method, RetVal, Arg, types } = protocol;
|
||||
|
||||
/**
|
||||
* The Promises Actor provides support for getting the list of live promises and
|
||||
* observing changes to their settlement state.
|
||||
*/
|
||||
let PromisesActor = protocol.ActorClass({
|
||||
typeName: "promises",
|
||||
|
||||
/**
|
||||
* @param conn DebuggerServerConnection.
|
||||
* @param parent TabActor|RootActor
|
||||
*/
|
||||
initialize: function(conn, parent) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
protocol.Actor.prototype.destroy.call(this, conn);
|
||||
},
|
||||
});
|
||||
|
||||
exports.PromisesActor = PromisesActor;
|
||||
|
||||
exports.PromisesFront = protocol.FrontClass(PromisesActor, {
|
||||
initialize: function(client, form) {
|
||||
protocol.Front.prototype.initialize.call(this, client, form);
|
||||
this.actorID = form.promisesActor;
|
||||
this.manage(this);
|
||||
}
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ const { Cu } = require("chrome");
|
||||
const { DebuggerServer } = require("devtools/server/main");
|
||||
const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
|
||||
const Debugger = require("Debugger");
|
||||
const { getOffsetColumn } = require("devtools/server/actors/common");
|
||||
const { GeneratedLocation, getOffsetColumn } = require("devtools/server/actors/common");
|
||||
const promise = require("promise");
|
||||
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
@@ -339,25 +339,25 @@ TracerActor.prototype = {
|
||||
if (aFrame.script) {
|
||||
let sources = this._parent.threadActor.sources;
|
||||
|
||||
sourceMappedLocation = yield sources.getOriginalLocation({
|
||||
sourceActor: sources.createNonSourceMappedActor(aFrame.script.source),
|
||||
url: aFrame.script.source.url,
|
||||
line: aFrame.script.startLine,
|
||||
sourceMappedLocation = yield sources.getOriginalLocation(new GeneratedLocation(
|
||||
sources.createNonSourceMappedActor(aFrame.script.source),
|
||||
aFrame.script.startLine,
|
||||
// We should return the location of the start of the script, but
|
||||
// Debugger.Script does not provide complete start locations (bug
|
||||
// 901138). Instead, return the current offset (the location of the
|
||||
// first statement in the function).
|
||||
column: getOffsetColumn(aFrame.offset, aFrame.script)
|
||||
});
|
||||
getOffsetColumn(aFrame.offset, aFrame.script)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (this._requestsForTraceType.name) {
|
||||
if (sourceMappedLocation && sourceMappedLocation.name) {
|
||||
packet.name = sourceMappedLocation.name;
|
||||
if (sourceMappedLocation && sourceMappedLocation.originalName) {
|
||||
packet.name = sourceMappedLocation.originalName;
|
||||
} else {
|
||||
packet.name = name;
|
||||
}
|
||||
packet.name = name;
|
||||
}
|
||||
|
||||
if (this._requestsForTraceType.location) {
|
||||
@@ -365,10 +365,9 @@ TracerActor.prototype = {
|
||||
// Don't copy sourceMappedLocation directly because it
|
||||
// contains a reference to the source actor
|
||||
packet.location = {
|
||||
url: sourceMappedLocation.url,
|
||||
line: sourceMappedLocation.line,
|
||||
column: sourceMappedLocation.column,
|
||||
name: sourceMappedLocation.name
|
||||
url: sourceMappedLocation.originalUrl,
|
||||
line: sourceMappedLocation.originalLine,
|
||||
column: sourceMappedLocation.originalColumn
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,18 @@ ScriptStore.prototype = {
|
||||
return this._scripts.items;
|
||||
},
|
||||
|
||||
getScriptsBySourceActor(sourceActor) {
|
||||
return sourceActor.source ?
|
||||
this.getScriptsBySource(sourceActor.source) :
|
||||
this.getScriptsByURL(sourceActor._originalUrl);
|
||||
},
|
||||
|
||||
getScriptsBySourceActorAndLine(sourceActor, line) {
|
||||
return sourceActor.source ?
|
||||
this.getScriptsBySourceAndLine(sourceActor.source, line) :
|
||||
this.getScriptsByURLAndLine(sourceActor._originalUrl, line);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get all scripts produced from the given source.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,751 @@
|
||||
const Services = require("Services");
|
||||
const { Ci, Cu } = require("chrome");
|
||||
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
const EventEmitter = require("devtools/toolkit/event-emitter");
|
||||
const { dbg_assert, fetch } = require("devtools/toolkit/DevToolsUtils");
|
||||
const { OriginalLocation, GeneratedLocation, getOffsetColumn } = require("devtools/server/actors/common");
|
||||
const { resolve } = require("promise");
|
||||
|
||||
loader.lazyRequireGetter(this, "SourceActor", "devtools/server/actors/script", true);
|
||||
loader.lazyRequireGetter(this, "isEvalSource", "devtools/server/actors/script", true);
|
||||
loader.lazyRequireGetter(this, "SourceMapConsumer", "source-map", true);
|
||||
loader.lazyRequireGetter(this, "SourceMapGenerator", "source-map", true);
|
||||
|
||||
/**
|
||||
* Manages the sources for a thread. Handles source maps, locations in the
|
||||
* sources, etc for ThreadActors.
|
||||
*/
|
||||
function TabSources(threadActor, allowSourceFn=() => true) {
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
this._thread = threadActor;
|
||||
this._useSourceMaps = true;
|
||||
this._autoBlackBox = true;
|
||||
this._anonSourceMapId = 1;
|
||||
this.allowSource = source => {
|
||||
return !isHiddenSource(source) && allowSourceFn(source);
|
||||
}
|
||||
|
||||
this.blackBoxedSources = new Set();
|
||||
this.prettyPrintedSources = new Map();
|
||||
|
||||
// generated Debugger.Source -> promise of SourceMapConsumer
|
||||
this._sourceMaps = new Map();
|
||||
// sourceMapURL -> promise of SourceMapConsumer
|
||||
this._sourceMapCache = Object.create(null);
|
||||
// Debugger.Source -> SourceActor
|
||||
this._sourceActors = new Map();
|
||||
// url -> SourceActor
|
||||
this._sourceMappedSourceActors = Object.create(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches strings of the form "foo.min.js" or "foo-min.js", etc. If the regular
|
||||
* expression matches, we can be fairly sure that the source is minified, and
|
||||
* treat it as such.
|
||||
*/
|
||||
const MINIFIED_SOURCE_REGEXP = /\bmin\.js$/;
|
||||
|
||||
TabSources.prototype = {
|
||||
/**
|
||||
* Update preferences and clear out existing sources
|
||||
*/
|
||||
reconfigure: function(options) {
|
||||
if('useSourceMaps' in options) {
|
||||
this._useSourceMaps = options.useSourceMaps;
|
||||
}
|
||||
|
||||
if('autoBlackBox' in options) {
|
||||
this._autoBlackBox = options.autoBlackBox;
|
||||
}
|
||||
|
||||
this.reset();
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear existing sources so they are recreated on the next access.
|
||||
*
|
||||
* @param Object opts
|
||||
* Specify { sourceMaps: true } if you also want to clear
|
||||
* the source map cache (usually done on reload).
|
||||
*/
|
||||
reset: function(opts={}) {
|
||||
this._sourceActors = new Map();
|
||||
this._sourceMaps = new Map();
|
||||
this._sourceMappedSourceActors = Object.create(null);
|
||||
|
||||
if(opts.sourceMaps) {
|
||||
this._sourceMapCache = Object.create(null);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the source actor representing the `source` (or
|
||||
* `originalUrl`), creating one if none exists already. May return
|
||||
* null if the source is disallowed.
|
||||
*
|
||||
* @param Debugger.Source source
|
||||
* The source to make an actor for
|
||||
* @param String originalUrl
|
||||
* The original source URL of a sourcemapped source
|
||||
* @param optional Debguger.Source generatedSource
|
||||
* The generated source that introduced this source via source map,
|
||||
* if any.
|
||||
* @param optional String contentType
|
||||
* The content type of the source, if immediately available.
|
||||
* @returns a SourceActor representing the source or null.
|
||||
*/
|
||||
source: function ({ source, originalUrl, generatedSource,
|
||||
isInlineSource, contentType }) {
|
||||
dbg_assert(source || (originalUrl && generatedSource),
|
||||
"TabSources.prototype.source needs an originalUrl or a source");
|
||||
|
||||
if (source) {
|
||||
// If a source is passed, we are creating an actor for a real
|
||||
// source, which may or may not be sourcemapped.
|
||||
|
||||
if (!this.allowSource(source)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// It's a hack, but inline HTML scripts each have real sources,
|
||||
// but we want to represent all of them as one source as the
|
||||
// HTML page. The actor representing this fake HTML source is
|
||||
// stored in this array, which always has a URL, so check it
|
||||
// first.
|
||||
if (source.url in this._sourceMappedSourceActors) {
|
||||
return this._sourceMappedSourceActors[source.url];
|
||||
}
|
||||
|
||||
if (isInlineSource) {
|
||||
// If it's an inline source, the fake HTML source hasn't been
|
||||
// created yet (would have returned above), so flip this source
|
||||
// into a sourcemapped state by giving it an `originalUrl` which
|
||||
// is the HTML url.
|
||||
originalUrl = source.url;
|
||||
source = null;
|
||||
}
|
||||
else if (this._sourceActors.has(source)) {
|
||||
return this._sourceActors.get(source);
|
||||
}
|
||||
}
|
||||
else if (originalUrl) {
|
||||
// Not all "original" scripts are distinctly separate from the
|
||||
// generated script. Pretty-printed sources have a sourcemap for
|
||||
// themselves, so we need to make sure there a real source
|
||||
// doesn't already exist with this URL.
|
||||
for (let [source, actor] of this._sourceActors) {
|
||||
if (source.url === originalUrl) {
|
||||
return actor;
|
||||
}
|
||||
}
|
||||
|
||||
if (originalUrl in this._sourceMappedSourceActors) {
|
||||
return this._sourceMappedSourceActors[originalUrl];
|
||||
}
|
||||
}
|
||||
|
||||
let actor = new SourceActor({
|
||||
thread: this._thread,
|
||||
source: source,
|
||||
originalUrl: originalUrl,
|
||||
generatedSource: generatedSource,
|
||||
contentType: contentType
|
||||
});
|
||||
|
||||
let sourceActorStore = this._thread.sourceActorStore;
|
||||
var id = sourceActorStore.getReusableActorId(source, originalUrl);
|
||||
if (id) {
|
||||
actor.actorID = id;
|
||||
}
|
||||
|
||||
this._thread.threadLifetimePool.addActor(actor);
|
||||
sourceActorStore.setReusableActorId(source, originalUrl, actor.actorID);
|
||||
|
||||
if (this._autoBlackBox && this._isMinifiedURL(actor.url)) {
|
||||
this.blackBox(actor.url);
|
||||
}
|
||||
|
||||
if (source) {
|
||||
this._sourceActors.set(source, actor);
|
||||
}
|
||||
else {
|
||||
this._sourceMappedSourceActors[originalUrl] = actor;
|
||||
}
|
||||
|
||||
this._emitNewSource(actor);
|
||||
return actor;
|
||||
},
|
||||
|
||||
_emitNewSource: function(actor) {
|
||||
if(!actor.source) {
|
||||
// Always notify if we don't have a source because that means
|
||||
// it's something that has been sourcemapped, or it represents
|
||||
// the HTML file that contains inline sources.
|
||||
this.emit('newSource', actor);
|
||||
}
|
||||
else {
|
||||
// If sourcemapping is enabled and a source has sourcemaps, we
|
||||
// create `SourceActor` instances for both the original and
|
||||
// generated sources. The source actors for the generated
|
||||
// sources are only for internal use, however; breakpoints are
|
||||
// managed by these internal actors. We only want to notify the
|
||||
// user of the original sources though, so if the actor has a
|
||||
// `Debugger.Source` instance and a valid source map (meaning
|
||||
// it's a generated source), don't send the notification.
|
||||
this.fetchSourceMap(actor.source).then(map => {
|
||||
if(!map) {
|
||||
this.emit('newSource', actor);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
getSourceActor: function(source) {
|
||||
if (source.url in this._sourceMappedSourceActors) {
|
||||
return this._sourceMappedSourceActors[source.url];
|
||||
}
|
||||
|
||||
if (this._sourceActors.has(source)) {
|
||||
return this._sourceActors.get(source);
|
||||
}
|
||||
|
||||
throw new Error('getSource: could not find source actor for ' +
|
||||
(source.url || 'source'));
|
||||
},
|
||||
|
||||
getSourceActorByURL: function(url) {
|
||||
if (url) {
|
||||
for (let [source, actor] of this._sourceActors) {
|
||||
if (source.url === url) {
|
||||
return actor;
|
||||
}
|
||||
}
|
||||
|
||||
if (url in this._sourceMappedSourceActors) {
|
||||
return this._sourceMappedSourceActors[url];
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('getSourceByURL: could not find source for ' + url);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the URL likely points to a minified resource, false
|
||||
* otherwise.
|
||||
*
|
||||
* @param String aURL
|
||||
* The URL to test.
|
||||
* @returns Boolean
|
||||
*/
|
||||
_isMinifiedURL: function (aURL) {
|
||||
try {
|
||||
let url = Services.io.newURI(aURL, null, null)
|
||||
.QueryInterface(Ci.nsIURL);
|
||||
return MINIFIED_SOURCE_REGEXP.test(url.fileName);
|
||||
} catch (e) {
|
||||
// Not a valid URL so don't try to parse out the filename, just test the
|
||||
// whole thing with the minified source regexp.
|
||||
return MINIFIED_SOURCE_REGEXP.test(aURL);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a source actor representing this source. This ignores
|
||||
* source mapping and always returns an actor representing this real
|
||||
* source. Use `createSourceActors` if you want to respect source maps.
|
||||
*
|
||||
* @param Debugger.Source aSource
|
||||
* The source instance to create an actor for.
|
||||
* @returns SourceActor
|
||||
*/
|
||||
createNonSourceMappedActor: function (aSource) {
|
||||
// Don't use getSourceURL because we don't want to consider the
|
||||
// displayURL property if it's an eval source. We only want to
|
||||
// consider real URLs, otherwise if there is a URL but it's
|
||||
// invalid the code below will not set the content type, and we
|
||||
// will later try to fetch the contents of the URL to figure out
|
||||
// the content type, but it's a made up URL for eval sources.
|
||||
let url = isEvalSource(aSource) ? null : aSource.url;
|
||||
let spec = { source: aSource };
|
||||
|
||||
// XXX bug 915433: We can't rely on Debugger.Source.prototype.text
|
||||
// if the source is an HTML-embedded <script> tag. Since we don't
|
||||
// have an API implemented to detect whether this is the case, we
|
||||
// need to be conservative and only treat valid js files as real
|
||||
// sources. Otherwise, use the `originalUrl` property to treat it
|
||||
// as an HTML source that manages multiple inline sources.
|
||||
if (url) {
|
||||
try {
|
||||
let urlInfo = Services.io.newURI(url, null, null).QueryInterface(Ci.nsIURL);
|
||||
if (urlInfo.fileExtension === "html") {
|
||||
spec.isInlineSource = true;
|
||||
}
|
||||
else if (urlInfo.fileExtension === "js") {
|
||||
spec.contentType = "text/javascript";
|
||||
}
|
||||
} catch(ex) {
|
||||
// Not a valid URI.
|
||||
|
||||
// bug 1124536: fix getSourceText on scripts associated "javascript:SOURCE" urls
|
||||
// (e.g. 'evaluate(sandbox, sourcecode, "javascript:"+sourcecode)' )
|
||||
if (url.indexOf("javascript:") === 0) {
|
||||
spec.contentType = "text/javascript";
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Assume the content is javascript if there's no URL
|
||||
spec.contentType = "text/javascript";
|
||||
}
|
||||
|
||||
return this.source(spec);
|
||||
},
|
||||
|
||||
/**
|
||||
* This is an internal function that returns a promise of an array
|
||||
* of source actors representing all the source mapped sources of
|
||||
* `aSource`, or `null` if the source is not sourcemapped or
|
||||
* sourcemapping is disabled. Users should call `createSourceActors`
|
||||
* instead of this.
|
||||
*
|
||||
* @param Debugger.Source aSource
|
||||
* The source instance to create actors for.
|
||||
* @return Promise of an array of source actors
|
||||
*/
|
||||
_createSourceMappedActors: function (aSource) {
|
||||
if (!this._useSourceMaps || !aSource.sourceMapURL) {
|
||||
return resolve(null);
|
||||
}
|
||||
|
||||
return this.fetchSourceMap(aSource)
|
||||
.then(map => {
|
||||
if (map) {
|
||||
return [
|
||||
this.source({ originalUrl: s, generatedSource: aSource })
|
||||
for (s of map.sources)
|
||||
].filter(isNotNull);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates the source actors representing the appropriate sources
|
||||
* of `aSource`. If sourcemapped, returns actors for all of the original
|
||||
* sources, otherwise returns a 1-element array with the actor for
|
||||
* `aSource`.
|
||||
*
|
||||
* @param Debugger.Source aSource
|
||||
* The source instance to create actors for.
|
||||
* @param Promise of an array of source actors
|
||||
*/
|
||||
createSourceActors: function(aSource) {
|
||||
return this._createSourceMappedActors(aSource).then(actors => {
|
||||
let actor = this.createNonSourceMappedActor(aSource);
|
||||
return (actors || [actor]).filter(isNotNull);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a promise of a SourceMapConsumer for the source map for
|
||||
* `aSource`; if we already have such a promise extant, return that.
|
||||
* This will fetch the source map if we don't have a cached object
|
||||
* and source maps are enabled (see `_fetchSourceMap`).
|
||||
*
|
||||
* @param Debugger.Source aSource
|
||||
* The source instance to get sourcemaps for.
|
||||
* @return Promise of a SourceMapConsumer
|
||||
*/
|
||||
fetchSourceMap: function (aSource) {
|
||||
if (this._sourceMaps.has(aSource)) {
|
||||
return this._sourceMaps.get(aSource);
|
||||
}
|
||||
else if (!aSource || !aSource.sourceMapURL) {
|
||||
return resolve(null);
|
||||
}
|
||||
|
||||
let sourceMapURL = aSource.sourceMapURL;
|
||||
if (aSource.url) {
|
||||
sourceMapURL = this._normalize(sourceMapURL, aSource.url);
|
||||
}
|
||||
let result = this._fetchSourceMap(sourceMapURL, aSource.url);
|
||||
|
||||
// The promises in `_sourceMaps` must be the exact same instances
|
||||
// as returned by `_fetchSourceMap` for `clearSourceMapCache` to work.
|
||||
this._sourceMaps.set(aSource, result);
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a promise of a SourceMapConsumer for the source map for
|
||||
* `aSource`. The resolved result may be null if the source does not
|
||||
* have a source map or source maps are disabled.
|
||||
*/
|
||||
getSourceMap: function(aSource) {
|
||||
return resolve(this._sourceMaps.get(aSource));
|
||||
},
|
||||
|
||||
/**
|
||||
* Set a SourceMapConsumer for the source map for
|
||||
* |aSource|.
|
||||
*/
|
||||
setSourceMap: function(aSource, aMap) {
|
||||
this._sourceMaps.set(aSource, resolve(aMap));
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a promise of a SourceMapConsumer for the source map located at
|
||||
* |aAbsSourceMapURL|, which must be absolute. If there is already such a
|
||||
* promise extant, return it. This will not fetch if source maps are
|
||||
* disabled.
|
||||
*
|
||||
* @param string aAbsSourceMapURL
|
||||
* The source map URL, in absolute form, not relative.
|
||||
* @param string aScriptURL
|
||||
* When the source map URL is a data URI, there is no sourceRoot on the
|
||||
* source map, and the source map's sources are relative, we resolve
|
||||
* them from aScriptURL.
|
||||
*/
|
||||
_fetchSourceMap: function (aAbsSourceMapURL, aSourceURL) {
|
||||
if (!this._useSourceMaps) {
|
||||
return resolve(null);
|
||||
}
|
||||
else if (this._sourceMapCache[aAbsSourceMapURL]) {
|
||||
return this._sourceMapCache[aAbsSourceMapURL];
|
||||
}
|
||||
|
||||
let fetching = fetch(aAbsSourceMapURL, { loadFromCache: false })
|
||||
.then(({ content }) => {
|
||||
let map = new SourceMapConsumer(content);
|
||||
this._setSourceMapRoot(map, aAbsSourceMapURL, aSourceURL);
|
||||
return map;
|
||||
})
|
||||
.then(null, error => {
|
||||
if (!DevToolsUtils.reportingDisabled) {
|
||||
DevToolsUtils.reportException("TabSources.prototype._fetchSourceMap", error);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
this._sourceMapCache[aAbsSourceMapURL] = fetching;
|
||||
return fetching;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the source map's sourceRoot to be relative to the source map url.
|
||||
*/
|
||||
_setSourceMapRoot: function (aSourceMap, aAbsSourceMapURL, aScriptURL) {
|
||||
const base = this._dirname(
|
||||
aAbsSourceMapURL.indexOf("data:") === 0
|
||||
? aScriptURL
|
||||
: aAbsSourceMapURL);
|
||||
aSourceMap.sourceRoot = aSourceMap.sourceRoot
|
||||
? this._normalize(aSourceMap.sourceRoot, base)
|
||||
: base;
|
||||
},
|
||||
|
||||
_dirname: function (aPath) {
|
||||
return Services.io.newURI(
|
||||
".", null, Services.io.newURI(aPath, null, null)).spec;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears the source map cache. Source maps are cached by URL so
|
||||
* they can be reused across separate Debugger instances (once in
|
||||
* this cache, they will never be reparsed again). They are
|
||||
* also cached by Debugger.Source objects for usefulness. By default
|
||||
* this just removes the Debugger.Source cache, but you can remove
|
||||
* the lower-level URL cache with the `hard` option.
|
||||
*
|
||||
* @param aSourceMapURL string
|
||||
* The source map URL to uncache
|
||||
* @param opts object
|
||||
* An object with the following properties:
|
||||
* - hard: Also remove the lower-level URL cache, which will
|
||||
* make us completely forget about the source map.
|
||||
*/
|
||||
clearSourceMapCache: function(aSourceMapURL, opts = { hard: false }) {
|
||||
let oldSm = this._sourceMapCache[aSourceMapURL];
|
||||
|
||||
if (opts.hard) {
|
||||
delete this._sourceMapCache[aSourceMapURL];
|
||||
}
|
||||
|
||||
if (oldSm) {
|
||||
// Clear out the current cache so all sources will get the new one
|
||||
for (let [source, sm] of this._sourceMaps.entries()) {
|
||||
if (sm === oldSm) {
|
||||
this._sourceMaps.delete(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Forcefully change the source map of a source, changing the
|
||||
* sourceMapURL and installing the source map in the cache. This is
|
||||
* necessary to expose changes across Debugger instances
|
||||
* (pretty-printing is the use case). Generate a random url if one
|
||||
* isn't specified, allowing you to set "anonymous" source maps.
|
||||
*
|
||||
* @param aSource Debugger.Source
|
||||
* The source to change the sourceMapURL property
|
||||
* @param aUrl string
|
||||
* The source map URL (optional)
|
||||
* @param aMap SourceMapConsumer
|
||||
* The source map instance
|
||||
*/
|
||||
setSourceMapHard: function(aSource, aUrl, aMap) {
|
||||
let url = aUrl;
|
||||
if (!url) {
|
||||
// This is a littly hacky, but we want to forcefully set a
|
||||
// sourcemap regardless of sourcemap settings. We want to
|
||||
// literally change the sourceMapURL so that all debuggers will
|
||||
// get this and pretty-printing will Just Work (Debugger.Source
|
||||
// instances are per-debugger, so we can't key off that). To
|
||||
// avoid tons of work serializing the sourcemap into a data url,
|
||||
// just make a fake URL and stick the sourcemap there.
|
||||
url = "internal://sourcemap" + (this._anonSourceMapId++) + '/';
|
||||
}
|
||||
aSource.sourceMapURL = url;
|
||||
|
||||
// Forcefully set the sourcemap cache. This will be used even if
|
||||
// sourcemaps are disabled.
|
||||
this._sourceMapCache[url] = resolve(aMap);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the non-source-mapped location of the given Debugger.Frame. If the
|
||||
* frame does not have a script, the location's properties are all null.
|
||||
*
|
||||
* @param Debugger.Frame aFrame
|
||||
* The frame whose location we are getting.
|
||||
* @returns Object
|
||||
* Returns an object of the form { source, line, column }
|
||||
*/
|
||||
getFrameLocation: function (aFrame) {
|
||||
if (!aFrame || !aFrame.script) {
|
||||
return new GeneratedLocation();
|
||||
}
|
||||
return new GeneratedLocation(
|
||||
this.createNonSourceMappedActor(aFrame.script.source),
|
||||
aFrame.script.getOffsetLine(aFrame.offset),
|
||||
getOffsetColumn(aFrame.offset, aFrame.script)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a promise of the location in the original source if the source is
|
||||
* source mapped, otherwise a promise of the same location. This can
|
||||
* be called with a source from *any* Debugger instance and we make
|
||||
* sure to that it works properly, reusing source maps if already
|
||||
* fetched. Use this from any actor that needs sourcemapping.
|
||||
*/
|
||||
getOriginalLocation: function (generatedLocation) {
|
||||
let {
|
||||
generatedSourceActor,
|
||||
generatedLine,
|
||||
generatedColumn
|
||||
} = generatedLocation;
|
||||
let source = generatedSourceActor.source;
|
||||
let url = source ? source.url : generatedSourceActor._originalUrl;
|
||||
|
||||
// In certain scenarios the source map may have not been fetched
|
||||
// yet (or at least tied to this Debugger.Source instance), so use
|
||||
// `fetchSourceMap` instead of `getSourceMap`. This allows this
|
||||
// function to be called from anywere (across debuggers) and it
|
||||
// should just automatically work.
|
||||
return this.fetchSourceMap(source).then(map => {
|
||||
if (map) {
|
||||
let {
|
||||
source: originalUrl,
|
||||
line: originalLine,
|
||||
column: originalColumn,
|
||||
name: originalName
|
||||
} = map.originalPositionFor({
|
||||
line: generatedLine,
|
||||
column: generatedColumn == null ? Infinity : generatedColumn
|
||||
});
|
||||
|
||||
// Since the `Debugger.Source` instance may come from a
|
||||
// different `Debugger` instance (any actor can call this
|
||||
// method), we can't rely on any of the source discovery
|
||||
// setup (`_discoverSources`, etc) to have been run yet. So
|
||||
// we have to assume that the actor may not already exist,
|
||||
// and we might need to create it, so use `source` and give
|
||||
// it the required parameters for a sourcemapped source.
|
||||
return new OriginalLocation(
|
||||
originalUrl ? this.source({
|
||||
originalUrl: originalUrl,
|
||||
generatedSource: source
|
||||
}) : null,
|
||||
originalLine,
|
||||
originalColumn,
|
||||
originalName
|
||||
);
|
||||
}
|
||||
|
||||
// No source map
|
||||
return OriginalLocation.fromGeneratedLocation(generatedLocation);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a promise of the location in the generated source corresponding to
|
||||
* the original source and line given.
|
||||
*
|
||||
* When we pass a script S representing generated code to `sourceMap`,
|
||||
* above, that returns a promise P. The process of resolving P populates
|
||||
* the tables this function uses; thus, it won't know that S's original
|
||||
* source URLs map to S until P is resolved.
|
||||
*/
|
||||
getGeneratedLocation: function (originalLocation) {
|
||||
let { originalSourceActor } = originalLocation;
|
||||
|
||||
// Both original sources and normal sources could have sourcemaps,
|
||||
// because normal sources can be pretty-printed which generates a
|
||||
// sourcemap for itself. Check both of the source properties to make it work
|
||||
// for both kinds of sources.
|
||||
let source = originalSourceActor.source || originalSourceActor.generatedSource;
|
||||
|
||||
// See comment about `fetchSourceMap` in `getOriginalLocation`.
|
||||
return this.fetchSourceMap(source).then((map) => {
|
||||
if (map) {
|
||||
let {
|
||||
originalLine,
|
||||
originalColumn
|
||||
} = originalLocation;
|
||||
|
||||
let {
|
||||
line: generatedLine,
|
||||
column: generatedColumn
|
||||
} = map.generatedPositionFor({
|
||||
source: originalSourceActor.url,
|
||||
line: originalLine,
|
||||
column: originalColumn == null ? 0 : originalColumn,
|
||||
bias: SourceMapConsumer.LEAST_UPPER_BOUND
|
||||
});
|
||||
|
||||
return new GeneratedLocation(
|
||||
this.createNonSourceMappedActor(source),
|
||||
generatedLine,
|
||||
generatedColumn
|
||||
);
|
||||
}
|
||||
|
||||
return GeneratedLocation.fromOriginalLocation(originalLocation);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if URL for the given source is black boxed.
|
||||
*
|
||||
* @param aURL String
|
||||
* The URL of the source which we are checking whether it is black
|
||||
* boxed or not.
|
||||
*/
|
||||
isBlackBoxed: function (aURL) {
|
||||
return this.blackBoxedSources.has(aURL);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the given source URL to the set of sources that are black boxed.
|
||||
*
|
||||
* @param aURL String
|
||||
* The URL of the source which we are black boxing.
|
||||
*/
|
||||
blackBox: function (aURL) {
|
||||
this.blackBoxedSources.add(aURL);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the given source URL to the set of sources that are black boxed.
|
||||
*
|
||||
* @param aURL String
|
||||
* The URL of the source which we are no longer black boxing.
|
||||
*/
|
||||
unblackBox: function (aURL) {
|
||||
this.blackBoxedSources.delete(aURL);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the given URL is pretty printed.
|
||||
*
|
||||
* @param aURL String
|
||||
* The URL of the source that might be pretty printed.
|
||||
*/
|
||||
isPrettyPrinted: function (aURL) {
|
||||
return this.prettyPrintedSources.has(aURL);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the given URL to the set of sources that are pretty printed.
|
||||
*
|
||||
* @param aURL String
|
||||
* The URL of the source to be pretty printed.
|
||||
*/
|
||||
prettyPrint: function (aURL, aIndent) {
|
||||
this.prettyPrintedSources.set(aURL, aIndent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the indent the given URL was pretty printed by.
|
||||
*/
|
||||
prettyPrintIndent: function (aURL) {
|
||||
return this.prettyPrintedSources.get(aURL);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the given URL from the set of sources that are pretty printed.
|
||||
*
|
||||
* @param aURL String
|
||||
* The URL of the source that is no longer pretty printed.
|
||||
*/
|
||||
disablePrettyPrint: function (aURL) {
|
||||
this.prettyPrintedSources.delete(aURL);
|
||||
},
|
||||
|
||||
/**
|
||||
* Normalize multiple relative paths towards the base paths on the right.
|
||||
*/
|
||||
_normalize: function (...aURLs) {
|
||||
dbg_assert(aURLs.length > 1, "Should have more than 1 URL");
|
||||
let base = Services.io.newURI(aURLs.pop(), null, null);
|
||||
let url;
|
||||
while ((url = aURLs.pop())) {
|
||||
base = Services.io.newURI(url, null, base);
|
||||
}
|
||||
return base.spec;
|
||||
},
|
||||
|
||||
iter: function () {
|
||||
let actors = Object.keys(this._sourceMappedSourceActors).map(k => {
|
||||
return this._sourceMappedSourceActors[k];
|
||||
});
|
||||
for (let actor of this._sourceActors.values()) {
|
||||
if (!this._sourceMaps.has(actor.source)) {
|
||||
actors.push(actor);
|
||||
}
|
||||
}
|
||||
return actors;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Checks if a source should never be displayed to the user because
|
||||
* it's either internal or we don't support in the UI yet.
|
||||
*/
|
||||
function isHiddenSource(aSource) {
|
||||
// Ignore the internal Function.prototype script
|
||||
return aSource.text === '() {\n}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if its argument is not null.
|
||||
*/
|
||||
function isNotNull(aThing) {
|
||||
return aThing !== null;
|
||||
}
|
||||
|
||||
exports.TabSources = TabSources;
|
||||
exports.isHiddenSource = isHiddenSource;
|
||||
@@ -13,6 +13,7 @@ let { RootActor } = require("devtools/server/actors/root");
|
||||
let { DebuggerServer } = require("devtools/server/main");
|
||||
let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
let { dbg_assert } = DevToolsUtils;
|
||||
let { TabSources, isHiddenSource } = require("./utils/TabSources");
|
||||
let makeDebugger = require("./utils/make-debugger");
|
||||
let mapURIToAddonID = require("./utils/map-uri-to-addon-id");
|
||||
|
||||
@@ -598,6 +599,7 @@ function TabActor(aConnection)
|
||||
// A map of actor names to actor instances provided by extensions.
|
||||
this._extraActors = {};
|
||||
this._exited = false;
|
||||
this._sources = null;
|
||||
|
||||
// Map of DOM stylesheets to StyleSheetActors
|
||||
this._styleSheetActors = new Map();
|
||||
@@ -767,6 +769,14 @@ TabActor.prototype = {
|
||||
return null;
|
||||
},
|
||||
|
||||
get sources() {
|
||||
if (!this._sources) {
|
||||
dbg_assert(this.threadActor, "threadActor should exist when creating sources.");
|
||||
this._sources = new TabSources(this.threadActor);
|
||||
}
|
||||
return this._sources;
|
||||
},
|
||||
|
||||
/**
|
||||
* This is called by BrowserTabList.getList for existing tab actors prior to
|
||||
* calling |form| below. It can be used to do any async work that may be
|
||||
@@ -1109,6 +1119,7 @@ TabActor.prototype = {
|
||||
this._contextPool = null;
|
||||
this.threadActor.exit();
|
||||
this.threadActor = null;
|
||||
this._sources = null;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1429,6 +1440,7 @@ TabActor.prototype = {
|
||||
// TODO bug 997119: move that code to ThreadActor by listening to window-ready
|
||||
let threadActor = this.threadActor;
|
||||
if (isTopLevel) {
|
||||
this.sources.reset({ sourceMaps: true });
|
||||
threadActor.clearDebuggees();
|
||||
if (threadActor.dbg) {
|
||||
threadActor.dbg.enabled = true;
|
||||
@@ -1877,6 +1889,15 @@ BrowserAddonActor.prototype = {
|
||||
return this._global;
|
||||
},
|
||||
|
||||
get sources() {
|
||||
if (!this._sources) {
|
||||
dbg_assert(this.threadActor, "threadActor should exist when creating sources.");
|
||||
this._sources = new TabSources(this._threadActor, this._allowSource);
|
||||
}
|
||||
return this._sources;
|
||||
},
|
||||
|
||||
|
||||
form: function BAA_form() {
|
||||
dbg_assert(this.actorID, "addon should have an actorID.");
|
||||
if (!this._consoleActor) {
|
||||
@@ -1957,6 +1978,7 @@ BrowserAddonActor.prototype = {
|
||||
this._contextPool.removeActor(this._threadActor);
|
||||
|
||||
this._threadActor = null;
|
||||
this._sources = null;
|
||||
|
||||
return { type: "detached" };
|
||||
},
|
||||
@@ -2030,6 +2052,20 @@ BrowserAddonActor.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Override the eligibility check for scripts and sources to make
|
||||
* sure every script and source with a URL is stored when debugging
|
||||
* add-ons.
|
||||
*/
|
||||
_allowSource: function(aSource) {
|
||||
// XPIProvider.jsm evals some code in every add-on's bootstrap.js. Hide it.
|
||||
if (aSource.url === "resource://gre/modules/addons/XPIProvider.jsm") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Yield the current set of globals associated with this addon that should be
|
||||
* added as debuggees.
|
||||
|
||||
@@ -23,3 +23,4 @@ let server = devtools.require("devtools/server/main");
|
||||
|
||||
this.DebuggerServer = server.DebuggerServer;
|
||||
this.ActorPool = server.ActorPool;
|
||||
this.OriginalLocation = server.OriginalLocation;
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
*/
|
||||
let { Ci, Cc, CC, Cu, Cr } = require("chrome");
|
||||
let Services = require("Services");
|
||||
let { ActorPool, RegisteredActorFactory, ObservedActorFactory } = require("devtools/server/actors/common");
|
||||
let { ActorPool, OriginalLocation, RegisteredActorFactory,
|
||||
ObservedActorFactory } = require("devtools/server/actors/common");
|
||||
let { LocalDebuggerTransport, ChildDebuggerTransport } =
|
||||
require("devtools/toolkit/transport/transport");
|
||||
let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
@@ -537,6 +538,11 @@ var DebuggerServer = {
|
||||
constructor: "AnimationsActor",
|
||||
type: { tab: true }
|
||||
});
|
||||
this.registerModule("devtools/server/actors/promises", {
|
||||
prefix: "promises",
|
||||
constructor: "PromisesActor",
|
||||
type: { global: true, tab: true }
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1130,9 +1136,14 @@ EventEmitter.decorate(DebuggerServer);
|
||||
|
||||
if (this.exports) {
|
||||
exports.DebuggerServer = DebuggerServer;
|
||||
exports.ActorPool = ActorPool;
|
||||
exports.OriginalLocation = OriginalLocation;
|
||||
}
|
||||
|
||||
// Needed on B2G (See header note)
|
||||
this.DebuggerServer = DebuggerServer;
|
||||
this.ActorPool = ActorPool;
|
||||
this.OriginalLocation = OriginalLocation;
|
||||
|
||||
// When using DebuggerServer.addActors, some symbols are expected to be in
|
||||
// the scope of the added actor even before the corresponding modules are
|
||||
@@ -1143,11 +1154,6 @@ includes.forEach(name => {
|
||||
DebuggerServer[name] = this[name];
|
||||
});
|
||||
|
||||
// Export ActorPool for requirers of main.js
|
||||
if (this.exports) {
|
||||
exports.ActorPool = ActorPool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DebuggerServerConnection.
|
||||
*
|
||||
|
||||
@@ -75,6 +75,7 @@ EXTRA_JS_MODULES.devtools.server.actors += [
|
||||
'actors/preference.js',
|
||||
'actors/pretty-print-worker.js',
|
||||
'actors/profiler.js',
|
||||
'actors/promises.js',
|
||||
'actors/root.js',
|
||||
'actors/script.js',
|
||||
'actors/settings.js',
|
||||
@@ -99,6 +100,7 @@ EXTRA_JS_MODULES.devtools.server.actors.utils += [
|
||||
'actors/utils/map-uri-to-addon-id.js',
|
||||
'actors/utils/ScriptStore.js',
|
||||
'actors/utils/stack.js',
|
||||
'actors/utils/TabSources.js'
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
@@ -209,6 +209,20 @@ function initTestDebuggerServer(aServer = DebuggerServer)
|
||||
aServer.init(function () { return true; });
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the testing debugger server with a tab whose title is |title|.
|
||||
*/
|
||||
function startTestDebuggerServer(title, server = DebuggerServer) {
|
||||
initTestDebuggerServer(server);
|
||||
addTestGlobal(title);
|
||||
DebuggerServer.addTabActors();
|
||||
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
let client = new DebuggerClient(transport);
|
||||
|
||||
return connect(client).then(() => client);
|
||||
}
|
||||
|
||||
function initTestTracerServer(aServer = DebuggerServer)
|
||||
{
|
||||
aServer.registerModule("xpcshell-test/testactors");
|
||||
@@ -246,6 +260,11 @@ function get_chrome_actors(callback)
|
||||
});
|
||||
}
|
||||
|
||||
function getChromeActors(client, server = DebuggerServer) {
|
||||
server.allowChromeProcess = true;
|
||||
return client.getProcess().then(response => response.form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a relative file path and returns the absolute file url for it.
|
||||
*/
|
||||
|
||||
@@ -21,13 +21,13 @@ function run_test()
|
||||
function test_get_actor() {
|
||||
let bpStore = new BreakpointActorMap();
|
||||
let location = {
|
||||
sourceActor: { actor: 'actor1' },
|
||||
line: 3
|
||||
originalSourceActor: { actor: 'actor1' },
|
||||
originalLine: 3
|
||||
};
|
||||
let columnLocation = {
|
||||
sourceActor: { actor: 'actor2' },
|
||||
line: 5,
|
||||
column: 15
|
||||
originalSourceActor: { actor: 'actor2' },
|
||||
originalLine: 5,
|
||||
originalColumn: 15
|
||||
};
|
||||
|
||||
// Shouldn't have breakpoint
|
||||
@@ -59,9 +59,9 @@ function test_set_actor() {
|
||||
// Breakpoint with column
|
||||
let bpStore = new BreakpointActorMap();
|
||||
let location = {
|
||||
sourceActor: { actor: 'actor1' },
|
||||
line: 10,
|
||||
column: 9
|
||||
originalSourceActor: { actor: 'actor1' },
|
||||
originalLine: 10,
|
||||
originalColumn: 9
|
||||
};
|
||||
bpStore.setActor(location, {});
|
||||
do_check_true(!!bpStore.getActor(location),
|
||||
@@ -69,8 +69,8 @@ function test_set_actor() {
|
||||
|
||||
// Breakpoint without column (whole line breakpoint)
|
||||
location = {
|
||||
sourceActor: { actor: 'actor2' },
|
||||
line: 103
|
||||
originalSourceActor: { actor: 'actor2' },
|
||||
originalLine: 103
|
||||
};
|
||||
bpStore.setActor(location, {});
|
||||
do_check_true(!!bpStore.getActor(location),
|
||||
@@ -81,9 +81,9 @@ function test_delete_actor() {
|
||||
// Breakpoint with column
|
||||
let bpStore = new BreakpointActorMap();
|
||||
let location = {
|
||||
sourceActor: { actor: 'actor1' },
|
||||
line: 10,
|
||||
column: 9
|
||||
originalSourceActor: { actor: 'actor1' },
|
||||
originalLine: 10,
|
||||
originalColumn: 9
|
||||
};
|
||||
bpStore.setActor(location, {});
|
||||
bpStore.deleteActor(location);
|
||||
@@ -92,8 +92,8 @@ function test_delete_actor() {
|
||||
|
||||
// Breakpoint without column (whole line breakpoint)
|
||||
location = {
|
||||
sourceActor: { actor: 'actor2' },
|
||||
line: 103
|
||||
originalSourceActor: { actor: 'actor2' },
|
||||
originalLine: 103
|
||||
};
|
||||
bpStore.setActor(location, {});
|
||||
bpStore.deleteActor(location);
|
||||
@@ -103,14 +103,14 @@ function test_delete_actor() {
|
||||
|
||||
function test_find_actors() {
|
||||
let bps = [
|
||||
{ sourceActor: { actor: "actor1" }, line: 10 },
|
||||
{ sourceActor: { actor: "actor1" }, line: 10, column: 3 },
|
||||
{ sourceActor: { actor: "actor1" }, line: 10, column: 10 },
|
||||
{ sourceActor: { actor: "actor1" }, line: 23, column: 89 },
|
||||
{ sourceActor: { actor: "actor2" }, line: 10, column: 1 },
|
||||
{ sourceActor: { actor: "actor2" }, line: 20, column: 5 },
|
||||
{ sourceActor: { actor: "actor2" }, line: 30, column: 34 },
|
||||
{ sourceActor: { actor: "actor2" }, line: 40, column: 56 }
|
||||
{ originalSourceActor: { actor: "actor1" }, originalLine: 10 },
|
||||
{ originalSourceActor: { actor: "actor1" }, originalLine: 10, originalColumn: 3 },
|
||||
{ originalSourceActor: { actor: "actor1" }, originalLine: 10, originalColumn: 10 },
|
||||
{ originalSourceActor: { actor: "actor1" }, originalLine: 23, originalColumn: 89 },
|
||||
{ originalSourceActor: { actor: "actor2" }, originalLine: 10, originalColumn: 1 },
|
||||
{ originalSourceActor: { actor: "actor2" }, originalLine: 20, originalColumn: 5 },
|
||||
{ originalSourceActor: { actor: "actor2" }, originalLine: 30, originalColumn: 34 },
|
||||
{ originalSourceActor: { actor: "actor2" }, originalLine: 40, originalColumn: 56 }
|
||||
];
|
||||
|
||||
let bpStore = new BreakpointActorMap();
|
||||
@@ -130,8 +130,8 @@ function test_find_actors() {
|
||||
|
||||
// Breakpoints by URL
|
||||
|
||||
bpSet = new Set(bps.filter(bp => { return bp.sourceActor.actorID === "actor1" }));
|
||||
for (let bp of bpStore.findActors({ sourceActor: { actorID: "actor1" } })) {
|
||||
bpSet = new Set(bps.filter(bp => { return bp.originalSourceActor.actorID === "actor1" }));
|
||||
for (let bp of bpStore.findActors({ originalSourceActor: { actorID: "actor1" } })) {
|
||||
bpSet.delete(bp);
|
||||
}
|
||||
do_check_eq(bpSet.size, 0,
|
||||
@@ -139,15 +139,15 @@ function test_find_actors() {
|
||||
|
||||
// Breakpoints by URL and line
|
||||
|
||||
bpSet = new Set(bps.filter(bp => { return bp.sourceActor.actorID === "actor1" && bp.line === 10; }));
|
||||
bpSet = new Set(bps.filter(bp => { return bp.originalSourceActor.actorID === "actor1" && bp.originalLine === 10; }));
|
||||
let first = true;
|
||||
for (let bp of bpStore.findActors({ sourceActor: { actorID: "actor1" }, line: 10 })) {
|
||||
for (let bp of bpStore.findActors({ originalSourceActor: { actorID: "actor1" }, originalLine: 10 })) {
|
||||
if (first) {
|
||||
do_check_eq(bp.column, undefined,
|
||||
do_check_eq(bp.originalColumn, undefined,
|
||||
"Should always get the whole line breakpoint first");
|
||||
first = false;
|
||||
} else {
|
||||
do_check_neq(bp.column, undefined,
|
||||
do_check_neq(bp.originalColumn, undefined,
|
||||
"Should not get the whole line breakpoint any time other than first.");
|
||||
}
|
||||
bpSet.delete(bp);
|
||||
@@ -161,9 +161,9 @@ function test_duplicate_actors() {
|
||||
|
||||
// Breakpoint with column
|
||||
let location = {
|
||||
sourceActor: { actorID: "foo-actor" },
|
||||
line: 10,
|
||||
column: 9
|
||||
originalSourceActor: { actorID: "foo-actor" },
|
||||
originalLine: 10,
|
||||
originalColumn: 9
|
||||
};
|
||||
bpStore.setActor(location, {});
|
||||
bpStore.setActor(location, {});
|
||||
@@ -172,8 +172,8 @@ function test_duplicate_actors() {
|
||||
|
||||
// Breakpoint without column (whole line breakpoint)
|
||||
location = {
|
||||
sourceActor: { actorID: "foo-actor" },
|
||||
line: 15
|
||||
originalSourceActor: { actorID: "foo-actor" },
|
||||
originalLine: 15
|
||||
};
|
||||
bpStore.setActor(location, {});
|
||||
bpStore.setActor(location, {});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Check conditional breakpoint when condition throws and make sure it is ignored
|
||||
* Check conditional breakpoint when condition throws and make sure it pauses
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
@@ -33,8 +33,8 @@ function test_simple_breakpoint()
|
||||
}, function (aResponse, bpClient) {
|
||||
gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
|
||||
// Check the return value.
|
||||
do_check_eq(aPacket.why.type, "debuggerStatement");
|
||||
do_check_eq(aPacket.frame.where.line, 4);
|
||||
do_check_eq(aPacket.why.type, "breakpointConditionThrown");
|
||||
do_check_eq(aPacket.frame.where.line, 3);
|
||||
|
||||
// Remove the breakpoint.
|
||||
bpClient.remove(function (aResponse) {
|
||||
@@ -53,8 +53,7 @@ function test_simple_breakpoint()
|
||||
|
||||
Components.utils.evalInSandbox("debugger;\n" + // 1
|
||||
"var a = 1;\n" + // 2
|
||||
"var b = 2;\n" + // 3
|
||||
"debugger;", // 4
|
||||
"var b = 2;\n", // 3
|
||||
gDebuggee,
|
||||
"1.8",
|
||||
"test.js",
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that the PromisesActor exists in the TabActors and ChromeActors.
|
||||
*/
|
||||
|
||||
add_task(function*() {
|
||||
let client = yield startTestDebuggerServer("promises-actor-test");
|
||||
|
||||
let response = yield listTabs(client);
|
||||
let targetTab = findTab(response.tabs, "promises-actor-test");
|
||||
ok(targetTab, "Found our target tab.")
|
||||
|
||||
// Attach to the TabActor and check the response
|
||||
client.request({ to: targetTab.actor, type: "attach" }, response => {
|
||||
ok(!("error" in response), "Expect no error in response.");
|
||||
ok(response.from, targetTab.actor,
|
||||
"Expect the target TabActor in response form field.");
|
||||
ok(response.type, "tabAttached",
|
||||
"Expect tabAttached in the response type.");
|
||||
is(typeof response.promisesActor === "string",
|
||||
"Should have a tab context PromisesActor.");
|
||||
});
|
||||
|
||||
let chromeActors = yield getChromeActors(client);
|
||||
ok(typeof chromeActors.promisesActor === "string",
|
||||
"Should have a chrome context PromisesActor.");
|
||||
});
|
||||
@@ -5,6 +5,7 @@ const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/se
|
||||
const { RootActor } = require("devtools/server/actors/root");
|
||||
const { ThreadActor } = require("devtools/server/actors/script");
|
||||
const { DebuggerServer } = require("devtools/server/main");
|
||||
const { TabSources } = require("devtools/server/actors/utils/TabSources");
|
||||
const promise = require("promise");
|
||||
const makeDebugger = require("devtools/server/actors/utils/make-debugger");
|
||||
|
||||
@@ -91,6 +92,13 @@ TestTabActor.prototype = {
|
||||
return this._global.__name;
|
||||
},
|
||||
|
||||
get sources() {
|
||||
if (!this._sources) {
|
||||
this._sources = new TabSources(this.threadActor);
|
||||
}
|
||||
return this._sources;
|
||||
},
|
||||
|
||||
form: function() {
|
||||
let response = { actor: this.actorID, title: this._global.__name };
|
||||
|
||||
@@ -124,6 +132,7 @@ TestTabActor.prototype = {
|
||||
},
|
||||
|
||||
onReload: function(aRequest) {
|
||||
this.sources.reset({ sourceMaps: true });
|
||||
this.threadActor.clearDebuggees();
|
||||
this.threadActor.dbg.addDebuggees();
|
||||
return {};
|
||||
|
||||
@@ -66,6 +66,7 @@ support-files =
|
||||
[test_eval-03.js]
|
||||
[test_eval-04.js]
|
||||
[test_eval-05.js]
|
||||
[test_promises_actor_exist.js]
|
||||
[test_protocol_abort.js]
|
||||
[test_protocol_async.js]
|
||||
[test_protocol_children.js]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -126,6 +126,113 @@ define('test/source-map/util', ['require', 'exports', 'module' , 'lib/source-ma
|
||||
sourceRoot: '',
|
||||
mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
|
||||
};
|
||||
// This mapping is identical to above, but uses the indexed format instead.
|
||||
exports.indexedTestMap = {
|
||||
version: 3,
|
||||
file: 'min.js',
|
||||
sections: [
|
||||
{
|
||||
offset: {
|
||||
line: 0,
|
||||
column: 0
|
||||
},
|
||||
map: {
|
||||
version: 3,
|
||||
sources: [
|
||||
"one.js"
|
||||
],
|
||||
sourcesContent: [
|
||||
' ONE.foo = function (bar) {\n' +
|
||||
' return baz(bar);\n' +
|
||||
' };',
|
||||
],
|
||||
names: [
|
||||
"bar",
|
||||
"baz"
|
||||
],
|
||||
mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID",
|
||||
file: "min.js",
|
||||
sourceRoot: "/the/root"
|
||||
}
|
||||
},
|
||||
{
|
||||
offset: {
|
||||
line: 1,
|
||||
column: 0
|
||||
},
|
||||
map: {
|
||||
version: 3,
|
||||
sources: [
|
||||
"two.js"
|
||||
],
|
||||
sourcesContent: [
|
||||
' TWO.inc = function (n) {\n' +
|
||||
' return n + 1;\n' +
|
||||
' };'
|
||||
],
|
||||
names: [
|
||||
"n"
|
||||
],
|
||||
mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA",
|
||||
file: "min.js",
|
||||
sourceRoot: "/the/root"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
exports.indexedTestMapDifferentSourceRoots = {
|
||||
version: 3,
|
||||
file: 'min.js',
|
||||
sections: [
|
||||
{
|
||||
offset: {
|
||||
line: 0,
|
||||
column: 0
|
||||
},
|
||||
map: {
|
||||
version: 3,
|
||||
sources: [
|
||||
"one.js"
|
||||
],
|
||||
sourcesContent: [
|
||||
' ONE.foo = function (bar) {\n' +
|
||||
' return baz(bar);\n' +
|
||||
' };',
|
||||
],
|
||||
names: [
|
||||
"bar",
|
||||
"baz"
|
||||
],
|
||||
mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID",
|
||||
file: "min.js",
|
||||
sourceRoot: "/the/root"
|
||||
}
|
||||
},
|
||||
{
|
||||
offset: {
|
||||
line: 1,
|
||||
column: 0
|
||||
},
|
||||
map: {
|
||||
version: 3,
|
||||
sources: [
|
||||
"two.js"
|
||||
],
|
||||
sourcesContent: [
|
||||
' TWO.inc = function (n) {\n' +
|
||||
' return n + 1;\n' +
|
||||
' };'
|
||||
],
|
||||
names: [
|
||||
"n"
|
||||
],
|
||||
mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA",
|
||||
file: "min.js",
|
||||
sourceRoot: "/different/root"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
exports.testMapWithSourcesContent = {
|
||||
version: 3,
|
||||
file: 'min.js',
|
||||
@@ -168,12 +275,13 @@ define('test/source-map/util', ['require', 'exports', 'module' , 'lib/source-ma
|
||||
|
||||
|
||||
function assertMapping(generatedLine, generatedColumn, originalSource,
|
||||
originalLine, originalColumn, name, map, assert,
|
||||
originalLine, originalColumn, name, bias, map, assert,
|
||||
dontTestGenerated, dontTestOriginal) {
|
||||
if (!dontTestOriginal) {
|
||||
var origMapping = map.originalPositionFor({
|
||||
line: generatedLine,
|
||||
column: generatedColumn
|
||||
column: generatedColumn,
|
||||
bias: bias
|
||||
});
|
||||
assert.equal(origMapping.name, name,
|
||||
'Incorrect name, expected ' + JSON.stringify(name)
|
||||
@@ -206,7 +314,8 @@ define('test/source-map/util', ['require', 'exports', 'module' , 'lib/source-ma
|
||||
var genMapping = map.generatedPositionFor({
|
||||
source: originalSource,
|
||||
line: originalLine,
|
||||
column: originalColumn
|
||||
column: originalColumn,
|
||||
bias: bias
|
||||
});
|
||||
assert.equal(genMapping.line, generatedLine,
|
||||
'Incorrect line, expected ' + JSON.stringify(generatedLine)
|
||||
@@ -521,7 +630,7 @@ define('lib/source-map/util', ['require', 'exports', 'module' , ], function(requ
|
||||
return cmp;
|
||||
}
|
||||
|
||||
cmp = strcmp(mappingA.name, mappingB.name);
|
||||
cmp = mappingA.generatedColumn - mappingB.generatedColumn;
|
||||
if (cmp) {
|
||||
return cmp;
|
||||
}
|
||||
@@ -531,7 +640,7 @@ define('lib/source-map/util', ['require', 'exports', 'module' , ], function(requ
|
||||
return cmp;
|
||||
}
|
||||
|
||||
return mappingA.generatedColumn - mappingB.generatedColumn;
|
||||
return strcmp(mappingA.name, mappingB.name);
|
||||
};
|
||||
exports.compareByOriginalPositions = compareByOriginalPositions;
|
||||
|
||||
|
||||
@@ -19,9 +19,10 @@ define("test/source-map/test-base64-vlq", ["require", "exports", "module"], func
|
||||
exports['test normal encoding and decoding'] = function (assert, util) {
|
||||
var result = {};
|
||||
for (var i = -255; i < 256; i++) {
|
||||
base64VLQ.decode(base64VLQ.encode(i), result);
|
||||
var str = base64VLQ.encode(i);
|
||||
base64VLQ.decode(str, 0, result);
|
||||
assert.equal(result.value, i);
|
||||
assert.equal(result.rest, "");
|
||||
assert.equal(result.rest, str.length);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ define("test/source-map/test-binary-search", ["require", "exports", "module"], f
|
||||
return a - b;
|
||||
}
|
||||
|
||||
exports['test too high'] = function (assert, util) {
|
||||
exports['test too high with default (glb) bias'] = function (assert, util) {
|
||||
var needle = 30;
|
||||
var haystack = [2,4,6,8,10,12,14,16,18,20];
|
||||
|
||||
@@ -31,7 +31,7 @@ define("test/source-map/test-binary-search", ["require", "exports", "module"], f
|
||||
assert.equal(haystack[binarySearch.search(needle, haystack, numberCompare)], 20);
|
||||
};
|
||||
|
||||
exports['test too low'] = function (assert, util) {
|
||||
exports['test too low with default (glb) bias'] = function (assert, util) {
|
||||
var needle = 1;
|
||||
var haystack = [2,4,6,8,10,12,14,16,18,20];
|
||||
|
||||
@@ -42,6 +42,30 @@ define("test/source-map/test-binary-search", ["require", "exports", "module"], f
|
||||
assert.equal(binarySearch.search(needle, haystack, numberCompare), -1);
|
||||
};
|
||||
|
||||
exports['test too high with lub bias'] = function (assert, util) {
|
||||
var needle = 30;
|
||||
var haystack = [2,4,6,8,10,12,14,16,18,20];
|
||||
|
||||
assert.doesNotThrow(function () {
|
||||
binarySearch.search(needle, haystack, numberCompare);
|
||||
});
|
||||
|
||||
assert.equal(binarySearch.search(needle, haystack, numberCompare,
|
||||
binarySearch.LEAST_UPPER_BOUND), -1);
|
||||
};
|
||||
|
||||
exports['test too low with lub bias'] = function (assert, util) {
|
||||
var needle = 1;
|
||||
var haystack = [2,4,6,8,10,12,14,16,18,20];
|
||||
|
||||
assert.doesNotThrow(function () {
|
||||
binarySearch.search(needle, haystack, numberCompare);
|
||||
});
|
||||
|
||||
assert.equal(haystack[binarySearch.search(needle, haystack, numberCompare,
|
||||
binarySearch.LEAST_UPPER_BOUND)], 2);
|
||||
};
|
||||
|
||||
exports['test exact search'] = function (assert, util) {
|
||||
var needle = 4;
|
||||
var haystack = [2,4,6,8,10,12,14,16,18,20];
|
||||
@@ -49,13 +73,37 @@ define("test/source-map/test-binary-search", ["require", "exports", "module"], f
|
||||
assert.equal(haystack[binarySearch.search(needle, haystack, numberCompare)], 4);
|
||||
};
|
||||
|
||||
exports['test fuzzy search'] = function (assert, util) {
|
||||
exports['test fuzzy search with default (glb) bias'] = function (assert, util) {
|
||||
var needle = 19;
|
||||
var haystack = [2,4,6,8,10,12,14,16,18,20];
|
||||
|
||||
assert.equal(haystack[binarySearch.search(needle, haystack, numberCompare)], 18);
|
||||
};
|
||||
|
||||
exports['test fuzzy search with lub bias'] = function (assert, util) {
|
||||
var needle = 19;
|
||||
var haystack = [2,4,6,8,10,12,14,16,18,20];
|
||||
|
||||
assert.equal(haystack[binarySearch.search(needle, haystack, numberCompare,
|
||||
binarySearch.LEAST_UPPER_BOUND)], 20);
|
||||
};
|
||||
|
||||
exports['test multiple matches'] = function (assert, util) {
|
||||
var needle = 5;
|
||||
var haystack = [1, 1, 2, 5, 5, 5, 13, 21];
|
||||
|
||||
assert.equal(binarySearch.search(needle, haystack, numberCompare,
|
||||
binarySearch.LEAST_UPPER_BOUND), 3);
|
||||
};
|
||||
|
||||
exports['test multiple matches at the beginning'] = function (assert, util) {
|
||||
var needle = 1;
|
||||
var haystack = [1, 1, 2, 5, 5, 5, 13, 21];
|
||||
|
||||
assert.equal(binarySearch.search(needle, haystack, numberCompare,
|
||||
binarySearch.LEAST_UPPER_BOUND), 0);
|
||||
};
|
||||
|
||||
});
|
||||
function run_test() {
|
||||
runSourceMapTests('test/source-map/test-binary-search', do_throw);
|
||||
|
||||
@@ -56,34 +56,55 @@ define("test/source-map/test-dog-fooding", ["require", "exports", "module"], fun
|
||||
var smc = new SourceMapConsumer(smg.toString());
|
||||
|
||||
// Exact
|
||||
util.assertMapping(2, 2, '/wu/tang/gza.coffee', 1, 0, null, smc, assert);
|
||||
util.assertMapping(3, 2, '/wu/tang/gza.coffee', 2, 0, null, smc, assert);
|
||||
util.assertMapping(4, 2, '/wu/tang/gza.coffee', 3, 0, null, smc, assert);
|
||||
util.assertMapping(5, 2, '/wu/tang/gza.coffee', 4, 0, null, smc, assert);
|
||||
util.assertMapping(6, 12, '/wu/tang/gza.coffee', 5, 10, null, smc, assert);
|
||||
util.assertMapping(2, 2, '/wu/tang/gza.coffee', 1, 0, null, null, smc, assert);
|
||||
util.assertMapping(3, 2, '/wu/tang/gza.coffee', 2, 0, null, null, smc, assert);
|
||||
util.assertMapping(4, 2, '/wu/tang/gza.coffee', 3, 0, null, null, smc, assert);
|
||||
util.assertMapping(5, 2, '/wu/tang/gza.coffee', 4, 0, null, null, smc, assert);
|
||||
util.assertMapping(6, 12, '/wu/tang/gza.coffee', 5, 10, null, null, smc, assert);
|
||||
|
||||
// Fuzzy
|
||||
|
||||
// Generated to original
|
||||
util.assertMapping(2, 0, null, null, null, null, smc, assert, true);
|
||||
util.assertMapping(2, 9, '/wu/tang/gza.coffee', 1, 0, null, smc, assert, true);
|
||||
util.assertMapping(3, 0, null, null, null, null, smc, assert, true);
|
||||
util.assertMapping(3, 9, '/wu/tang/gza.coffee', 2, 0, null, smc, assert, true);
|
||||
util.assertMapping(4, 0, null, null, null, null, smc, assert, true);
|
||||
util.assertMapping(4, 9, '/wu/tang/gza.coffee', 3, 0, null, smc, assert, true);
|
||||
util.assertMapping(5, 0, null, null, null, null, smc, assert, true);
|
||||
util.assertMapping(5, 9, '/wu/tang/gza.coffee', 4, 0, null, smc, assert, true);
|
||||
util.assertMapping(6, 0, null, null, null, null, smc, assert, true);
|
||||
util.assertMapping(6, 9, null, null, null, null, smc, assert, true);
|
||||
util.assertMapping(6, 13, '/wu/tang/gza.coffee', 5, 10, null, smc, assert, true);
|
||||
// Generated to original with default (glb) bias.
|
||||
util.assertMapping(2, 0, null, null, null, null, null, smc, assert, true);
|
||||
util.assertMapping(2, 9, '/wu/tang/gza.coffee', 1, 0, null, null, smc, assert, true);
|
||||
util.assertMapping(3, 0, null, null, null, null, null, smc, assert, true);
|
||||
util.assertMapping(3, 9, '/wu/tang/gza.coffee', 2, 0, null, null, smc, assert, true);
|
||||
util.assertMapping(4, 0, null, null, null, null, null, smc, assert, true);
|
||||
util.assertMapping(4, 9, '/wu/tang/gza.coffee', 3, 0, null, null, smc, assert, true);
|
||||
util.assertMapping(5, 0, null, null, null, null, null, smc, assert, true);
|
||||
util.assertMapping(5, 9, '/wu/tang/gza.coffee', 4, 0, null, null, smc, assert, true);
|
||||
util.assertMapping(6, 0, null, null, null, null, null, smc, assert, true);
|
||||
util.assertMapping(6, 9, null, null, null, null, null, smc, assert, true);
|
||||
util.assertMapping(6, 13, '/wu/tang/gza.coffee', 5, 10, null, null, smc, assert, true);
|
||||
|
||||
// Original to generated
|
||||
util.assertMapping(2, 2, '/wu/tang/gza.coffee', 1, 1, null, smc, assert, null, true);
|
||||
util.assertMapping(3, 2, '/wu/tang/gza.coffee', 2, 3, null, smc, assert, null, true);
|
||||
util.assertMapping(4, 2, '/wu/tang/gza.coffee', 3, 6, null, smc, assert, null, true);
|
||||
util.assertMapping(5, 2, '/wu/tang/gza.coffee', 4, 9, null, smc, assert, null, true);
|
||||
util.assertMapping(5, 2, '/wu/tang/gza.coffee', 5, 9, null, smc, assert, null, true);
|
||||
util.assertMapping(6, 12, '/wu/tang/gza.coffee', 6, 19, null, smc, assert, null, true);
|
||||
// Generated to original with lub bias.
|
||||
util.assertMapping(2, 0, '/wu/tang/gza.coffee', 1, 0, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, true);
|
||||
util.assertMapping(2, 9, null, null, null, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, true);
|
||||
util.assertMapping(3, 0, '/wu/tang/gza.coffee', 2, 0, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, true);
|
||||
util.assertMapping(3, 9, null, null, null, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, true);
|
||||
util.assertMapping(4, 0, '/wu/tang/gza.coffee', 3, 0, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, true);
|
||||
util.assertMapping(4, 9, null, null, null, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, true);
|
||||
util.assertMapping(5, 0, '/wu/tang/gza.coffee', 4, 0, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, true);
|
||||
util.assertMapping(5, 9, null, null, null, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, true);
|
||||
util.assertMapping(6, 0, '/wu/tang/gza.coffee', 5, 10, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, true);
|
||||
util.assertMapping(6, 9, '/wu/tang/gza.coffee', 5, 10, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, true);
|
||||
util.assertMapping(6, 13, null, null, null, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, true);
|
||||
|
||||
// Original to generated with default (glb) bias
|
||||
util.assertMapping(2, 2, '/wu/tang/gza.coffee', 1, 1, null, null, smc, assert, null, true);
|
||||
util.assertMapping(3, 2, '/wu/tang/gza.coffee', 2, 3, null, null, smc, assert, null, true);
|
||||
util.assertMapping(4, 2, '/wu/tang/gza.coffee', 3, 6, null, null, smc, assert, null, true);
|
||||
util.assertMapping(5, 2, '/wu/tang/gza.coffee', 4, 9, null, null, smc, assert, null, true);
|
||||
util.assertMapping(5, 2, '/wu/tang/gza.coffee', 5, 9, null, null, smc, assert, null, true);
|
||||
util.assertMapping(6, 12, '/wu/tang/gza.coffee', 6, 19, null, null, smc, assert, null, true);
|
||||
|
||||
// Original to generated with lub bias.
|
||||
util.assertMapping(3, 2, '/wu/tang/gza.coffee', 1, 1, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, null, true);
|
||||
util.assertMapping(4, 2, '/wu/tang/gza.coffee', 2, 3, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, null, true);
|
||||
util.assertMapping(5, 2, '/wu/tang/gza.coffee', 3, 6, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, null, true);
|
||||
util.assertMapping(6, 12, '/wu/tang/gza.coffee', 4, 9, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, null, true);
|
||||
util.assertMapping(6, 12, '/wu/tang/gza.coffee', 5, 9, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, null, true);
|
||||
util.assertMapping(null, null, '/wu/tang/gza.coffee', 6, 19, null, SourceMapConsumer.LEAST_UPPER_BOUND, smc, assert, null, true);
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -15,6 +15,8 @@ Components.utils.import('resource://test/Utils.jsm');
|
||||
define("test/source-map/test-source-map-consumer", ["require", "exports", "module"], function (require, exports, module) {
|
||||
|
||||
var SourceMapConsumer = require('source-map/source-map-consumer').SourceMapConsumer;
|
||||
var IndexedSourceMapConsumer = require('source-map/source-map-consumer').IndexedSourceMapConsumer;
|
||||
var BasicSourceMapConsumer = require('source-map/source-map-consumer').BasicSourceMapConsumer;
|
||||
var SourceMapGenerator = require('source-map/source-map-generator').SourceMapGenerator;
|
||||
|
||||
exports['test that we can instantiate with a string or an object'] = function (assert, util) {
|
||||
@@ -26,6 +28,18 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
|
||||
});
|
||||
};
|
||||
|
||||
exports['test that the object returned from new SourceMapConsumer inherits from SourceMapConsumer'] = function (assert, util) {
|
||||
assert.ok(new SourceMapConsumer(util.testMap) instanceof SourceMapConsumer);
|
||||
}
|
||||
|
||||
exports['test that a BasicSourceMapConsumer is returned for sourcemaps without sections'] = function(assert, util) {
|
||||
assert.ok(new SourceMapConsumer(util.testMap) instanceof BasicSourceMapConsumer);
|
||||
};
|
||||
|
||||
exports['test that an IndexedSourceMapConsumer is returned for sourcemaps with sections'] = function(assert, util) {
|
||||
assert.ok(new SourceMapConsumer(util.indexedTestMap) instanceof IndexedSourceMapConsumer);
|
||||
};
|
||||
|
||||
exports['test that the `sources` field has the original sources'] = function (assert, util) {
|
||||
var map;
|
||||
var sources;
|
||||
@@ -36,6 +50,18 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
|
||||
assert.equal(sources[1], '/the/root/two.js');
|
||||
assert.equal(sources.length, 2);
|
||||
|
||||
map = new SourceMapConsumer(util.indexedTestMap);
|
||||
sources = map.sources;
|
||||
assert.equal(sources[0], '/the/root/one.js');
|
||||
assert.equal(sources[1], '/the/root/two.js');
|
||||
assert.equal(sources.length, 2);
|
||||
|
||||
map = new SourceMapConsumer(util.indexedTestMapDifferentSourceRoots);
|
||||
sources = map.sources;
|
||||
assert.equal(sources[0], '/the/root/one.js');
|
||||
assert.equal(sources[1], '/different/root/two.js');
|
||||
assert.equal(sources.length, 2);
|
||||
|
||||
map = new SourceMapConsumer(util.testMapNoSourceRoot);
|
||||
sources = map.sources;
|
||||
assert.equal(sources[0], 'one.js');
|
||||
@@ -101,34 +127,107 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
|
||||
exports['test mapping tokens back exactly'] = function (assert, util) {
|
||||
var map = new SourceMapConsumer(util.testMap);
|
||||
|
||||
util.assertMapping(1, 1, '/the/root/one.js', 1, 1, null, map, assert);
|
||||
util.assertMapping(1, 5, '/the/root/one.js', 1, 5, null, map, assert);
|
||||
util.assertMapping(1, 9, '/the/root/one.js', 1, 11, null, map, assert);
|
||||
util.assertMapping(1, 18, '/the/root/one.js', 1, 21, 'bar', map, assert);
|
||||
util.assertMapping(1, 21, '/the/root/one.js', 2, 3, null, map, assert);
|
||||
util.assertMapping(1, 28, '/the/root/one.js', 2, 10, 'baz', map, assert);
|
||||
util.assertMapping(1, 32, '/the/root/one.js', 2, 14, 'bar', map, assert);
|
||||
util.assertMapping(1, 1, '/the/root/one.js', 1, 1, null, null, map, assert);
|
||||
util.assertMapping(1, 5, '/the/root/one.js', 1, 5, null, null, map, assert);
|
||||
util.assertMapping(1, 9, '/the/root/one.js', 1, 11, null, null, map, assert);
|
||||
util.assertMapping(1, 18, '/the/root/one.js', 1, 21, 'bar', null, map, assert);
|
||||
util.assertMapping(1, 21, '/the/root/one.js', 2, 3, null, null, map, assert);
|
||||
util.assertMapping(1, 28, '/the/root/one.js', 2, 10, 'baz', null, map, assert);
|
||||
util.assertMapping(1, 32, '/the/root/one.js', 2, 14, 'bar', null, map, assert);
|
||||
|
||||
util.assertMapping(2, 1, '/the/root/two.js', 1, 1, null, map, assert);
|
||||
util.assertMapping(2, 5, '/the/root/two.js', 1, 5, null, map, assert);
|
||||
util.assertMapping(2, 9, '/the/root/two.js', 1, 11, null, map, assert);
|
||||
util.assertMapping(2, 18, '/the/root/two.js', 1, 21, 'n', map, assert);
|
||||
util.assertMapping(2, 21, '/the/root/two.js', 2, 3, null, map, assert);
|
||||
util.assertMapping(2, 28, '/the/root/two.js', 2, 10, 'n', map, assert);
|
||||
util.assertMapping(2, 1, '/the/root/two.js', 1, 1, null, null, map, assert);
|
||||
util.assertMapping(2, 5, '/the/root/two.js', 1, 5, null, null, map, assert);
|
||||
util.assertMapping(2, 9, '/the/root/two.js', 1, 11, null, null, map, assert);
|
||||
util.assertMapping(2, 18, '/the/root/two.js', 1, 21, 'n', null, map, assert);
|
||||
util.assertMapping(2, 21, '/the/root/two.js', 2, 3, null, null, map, assert);
|
||||
util.assertMapping(2, 28, '/the/root/two.js', 2, 10, 'n', null, map, assert);
|
||||
};
|
||||
|
||||
exports['test mapping tokens back exactly in indexed source map'] = function (assert, util) {
|
||||
var map = new SourceMapConsumer(util.indexedTestMap);
|
||||
|
||||
util.assertMapping(1, 1, '/the/root/one.js', 1, 1, null, null, map, assert);
|
||||
util.assertMapping(1, 5, '/the/root/one.js', 1, 5, null, null, map, assert);
|
||||
util.assertMapping(1, 9, '/the/root/one.js', 1, 11, null, null, map, assert);
|
||||
util.assertMapping(1, 18, '/the/root/one.js', 1, 21, 'bar', null, map, assert);
|
||||
util.assertMapping(1, 21, '/the/root/one.js', 2, 3, null, null, map, assert);
|
||||
util.assertMapping(1, 28, '/the/root/one.js', 2, 10, 'baz', null, map, assert);
|
||||
util.assertMapping(1, 32, '/the/root/one.js', 2, 14, 'bar', null, map, assert);
|
||||
|
||||
util.assertMapping(2, 1, '/the/root/two.js', 1, 1, null, null, map, assert);
|
||||
util.assertMapping(2, 5, '/the/root/two.js', 1, 5, null, null, map, assert);
|
||||
util.assertMapping(2, 9, '/the/root/two.js', 1, 11, null, null, map, assert);
|
||||
util.assertMapping(2, 18, '/the/root/two.js', 1, 21, 'n', null, map, assert);
|
||||
util.assertMapping(2, 21, '/the/root/two.js', 2, 3, null, null, map, assert);
|
||||
util.assertMapping(2, 28, '/the/root/two.js', 2, 10, 'n', null, map, assert);
|
||||
};
|
||||
|
||||
|
||||
exports['test mapping tokens back exactly'] = function (assert, util) {
|
||||
var map = new SourceMapConsumer(util.testMap);
|
||||
|
||||
util.assertMapping(1, 1, '/the/root/one.js', 1, 1, null, null, map, assert);
|
||||
util.assertMapping(1, 5, '/the/root/one.js', 1, 5, null, null, map, assert);
|
||||
util.assertMapping(1, 9, '/the/root/one.js', 1, 11, null, null, map, assert);
|
||||
util.assertMapping(1, 18, '/the/root/one.js', 1, 21, 'bar', null, map, assert);
|
||||
util.assertMapping(1, 21, '/the/root/one.js', 2, 3, null, null, map, assert);
|
||||
util.assertMapping(1, 28, '/the/root/one.js', 2, 10, 'baz', null, map, assert);
|
||||
util.assertMapping(1, 32, '/the/root/one.js', 2, 14, 'bar', null, map, assert);
|
||||
|
||||
util.assertMapping(2, 1, '/the/root/two.js', 1, 1, null, null, map, assert);
|
||||
util.assertMapping(2, 5, '/the/root/two.js', 1, 5, null, null, map, assert);
|
||||
util.assertMapping(2, 9, '/the/root/two.js', 1, 11, null, null, map, assert);
|
||||
util.assertMapping(2, 18, '/the/root/two.js', 1, 21, 'n', null, map, assert);
|
||||
util.assertMapping(2, 21, '/the/root/two.js', 2, 3, null, null, map, assert);
|
||||
util.assertMapping(2, 28, '/the/root/two.js', 2, 10, 'n', null, map, assert);
|
||||
};
|
||||
|
||||
exports['test mapping tokens fuzzy'] = function (assert, util) {
|
||||
var map = new SourceMapConsumer(util.testMap);
|
||||
|
||||
// Finding original positions
|
||||
util.assertMapping(1, 20, '/the/root/one.js', 1, 21, 'bar', map, assert, true);
|
||||
util.assertMapping(1, 30, '/the/root/one.js', 2, 10, 'baz', map, assert, true);
|
||||
util.assertMapping(2, 12, '/the/root/two.js', 1, 11, null, map, assert, true);
|
||||
// Finding original positions with default (glb) bias.
|
||||
util.assertMapping(1, 20, '/the/root/one.js', 1, 21, 'bar', null, map, assert, true);
|
||||
util.assertMapping(1, 30, '/the/root/one.js', 2, 10, 'baz', null, map, assert, true);
|
||||
util.assertMapping(2, 12, '/the/root/two.js', 1, 11, null, null, map, assert, true);
|
||||
|
||||
// Finding generated positions
|
||||
util.assertMapping(1, 18, '/the/root/one.js', 1, 22, 'bar', map, assert, null, true);
|
||||
util.assertMapping(1, 28, '/the/root/one.js', 2, 13, 'baz', map, assert, null, true);
|
||||
util.assertMapping(2, 9, '/the/root/two.js', 1, 16, null, map, assert, null, true);
|
||||
// Finding original positions with lub bias.
|
||||
util.assertMapping(1, 16, '/the/root/one.js', 1, 21, 'bar', SourceMapConsumer.LEAST_UPPER_BOUND, map, assert, true);
|
||||
util.assertMapping(1, 26, '/the/root/one.js', 2, 10, 'baz', SourceMapConsumer.LEAST_UPPER_BOUND, map, assert, true);
|
||||
util.assertMapping(2, 6, '/the/root/two.js', 1, 11, null, SourceMapConsumer.LEAST_UPPER_BOUND, map, assert, true);
|
||||
|
||||
// Finding generated positions with default (glb) bias.
|
||||
util.assertMapping(1, 18, '/the/root/one.js', 1, 22, 'bar', null, map, assert, null, true);
|
||||
util.assertMapping(1, 28, '/the/root/one.js', 2, 13, 'baz', null, map, assert, null, true);
|
||||
util.assertMapping(2, 9, '/the/root/two.js', 1, 16, null, null, map, assert, null, true);
|
||||
|
||||
// Finding generated positions with lub bias.
|
||||
util.assertMapping(1, 18, '/the/root/one.js', 1, 20, 'bar', SourceMapConsumer.LEAST_UPPER_BOUND, map, assert, null, true);
|
||||
util.assertMapping(1, 28, '/the/root/one.js', 2, 7, 'baz', SourceMapConsumer.LEAST_UPPER_BOUND, map, assert, null, true);
|
||||
util.assertMapping(2, 9, '/the/root/two.js', 1, 6, null, SourceMapConsumer.LEAST_UPPER_BOUND, map, assert, null, true);
|
||||
};
|
||||
|
||||
exports['test mapping tokens fuzzy in indexed source map'] = function (assert, util) {
|
||||
var map = new SourceMapConsumer(util.indexedTestMap);
|
||||
|
||||
// Finding original positions with default (glb) bias.
|
||||
util.assertMapping(1, 20, '/the/root/one.js', 1, 21, 'bar', null, map, assert, true);
|
||||
util.assertMapping(1, 30, '/the/root/one.js', 2, 10, 'baz', null, map, assert, true);
|
||||
util.assertMapping(2, 12, '/the/root/two.js', 1, 11, null, null, map, assert, true);
|
||||
|
||||
// Finding original positions with lub bias.
|
||||
util.assertMapping(1, 16, '/the/root/one.js', 1, 21, 'bar', SourceMapConsumer.LEAST_UPPER_BOUND, map, assert, true);
|
||||
util.assertMapping(1, 26, '/the/root/one.js', 2, 10, 'baz', SourceMapConsumer.LEAST_UPPER_BOUND, map, assert, true);
|
||||
util.assertMapping(2, 6, '/the/root/two.js', 1, 11, null, SourceMapConsumer.LEAST_UPPER_BOUND, map, assert, true);
|
||||
|
||||
// Finding generated positions with default (glb) bias.
|
||||
util.assertMapping(1, 18, '/the/root/one.js', 1, 22, 'bar', null, map, assert, null, true);
|
||||
util.assertMapping(1, 28, '/the/root/one.js', 2, 13, 'baz', null, map, assert, null, true);
|
||||
util.assertMapping(2, 9, '/the/root/two.js', 1, 16, null, null, map, assert, null, true);
|
||||
|
||||
// Finding generated positions with lub bias.
|
||||
util.assertMapping(1, 18, '/the/root/one.js', 1, 20, 'bar', SourceMapConsumer.LEAST_UPPER_BOUND, map, assert, null, true);
|
||||
util.assertMapping(1, 28, '/the/root/one.js', 2, 7, 'baz', SourceMapConsumer.LEAST_UPPER_BOUND, map, assert, null, true);
|
||||
util.assertMapping(2, 9, '/the/root/two.js', 1, 6, null, SourceMapConsumer.LEAST_UPPER_BOUND, map, assert, null, true);
|
||||
};
|
||||
|
||||
exports['test mappings and end of lines'] = function (assert, util) {
|
||||
@@ -145,14 +244,22 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
|
||||
generated: { line: 2, column: 2 },
|
||||
source: 'bar.js'
|
||||
});
|
||||
smg.addMapping({
|
||||
original: { line: 1, column: 1 },
|
||||
generated: { line: 1, column: 1 },
|
||||
source: 'baz.js'
|
||||
});
|
||||
|
||||
var map = SourceMapConsumer.fromSourceMap(smg);
|
||||
|
||||
// When finding original positions, mappings end at the end of the line.
|
||||
util.assertMapping(2, 1, null, null, null, null, map, assert, true)
|
||||
util.assertMapping(2, 1, null, null, null, null, null, map, assert, true)
|
||||
|
||||
// When finding generated positions, mappings do not end at the end of the line.
|
||||
util.assertMapping(1, 1, 'bar.js', 2, 1, null, map, assert, null, true);
|
||||
util.assertMapping(1, 1, 'bar.js', 2, 1, null, null, map, assert, null, true);
|
||||
|
||||
// When finding generated positions with, mappings end at the end of the source.
|
||||
util.assertMapping(null, null, 'bar.js', 3, 1, null, SourceMapConsumer.LEAST_UPPER_BOUND, map, assert, null, true);
|
||||
};
|
||||
|
||||
exports['test creating source map consumers with )]}\' prefix'] = function (assert, util) {
|
||||
@@ -193,6 +300,29 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
|
||||
});
|
||||
};
|
||||
|
||||
exports['test eachMapping for indexed source maps'] = function(assert, util) {
|
||||
var map = new SourceMapConsumer(util.indexedTestMap);
|
||||
var previousLine = -Infinity;
|
||||
var previousColumn = -Infinity;
|
||||
map.eachMapping(function (mapping) {
|
||||
assert.ok(mapping.generatedLine >= previousLine);
|
||||
|
||||
if (mapping.source) {
|
||||
assert.equal(mapping.source.indexOf(util.testMap.sourceRoot), 0);
|
||||
}
|
||||
|
||||
if (mapping.generatedLine === previousLine) {
|
||||
assert.ok(mapping.generatedColumn >= previousColumn);
|
||||
previousColumn = mapping.generatedColumn;
|
||||
}
|
||||
else {
|
||||
previousLine = mapping.generatedLine;
|
||||
previousColumn = -Infinity;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
exports['test iterating over mappings in a different order'] = function (assert, util) {
|
||||
var map = new SourceMapConsumer(util.testMap);
|
||||
var previousLine = -Infinity;
|
||||
@@ -221,6 +351,34 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
|
||||
}, null, SourceMapConsumer.ORIGINAL_ORDER);
|
||||
};
|
||||
|
||||
exports['test iterating over mappings in a different order in indexed source maps'] = function (assert, util) {
|
||||
var map = new SourceMapConsumer(util.indexedTestMap);
|
||||
var previousLine = -Infinity;
|
||||
var previousColumn = -Infinity;
|
||||
var previousSource = "";
|
||||
map.eachMapping(function (mapping) {
|
||||
assert.ok(mapping.source >= previousSource);
|
||||
|
||||
if (mapping.source === previousSource) {
|
||||
assert.ok(mapping.originalLine >= previousLine);
|
||||
|
||||
if (mapping.originalLine === previousLine) {
|
||||
assert.ok(mapping.originalColumn >= previousColumn);
|
||||
previousColumn = mapping.originalColumn;
|
||||
}
|
||||
else {
|
||||
previousLine = mapping.originalLine;
|
||||
previousColumn = -Infinity;
|
||||
}
|
||||
}
|
||||
else {
|
||||
previousSource = mapping.source;
|
||||
previousLine = -Infinity;
|
||||
previousColumn = -Infinity;
|
||||
}
|
||||
}, null, SourceMapConsumer.ORIGINAL_ORDER);
|
||||
};
|
||||
|
||||
exports['test that we can set the context for `this` in eachMapping'] = function (assert, util) {
|
||||
var map = new SourceMapConsumer(util.testMap);
|
||||
var context = {};
|
||||
@@ -229,6 +387,14 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
|
||||
}, context);
|
||||
};
|
||||
|
||||
exports['test that we can set the context for `this` in eachMapping in indexed source maps'] = function (assert, util) {
|
||||
var map = new SourceMapConsumer(util.indexedTestMap);
|
||||
var context = {};
|
||||
map.eachMapping(function () {
|
||||
assert.equal(this, context);
|
||||
}, context);
|
||||
};
|
||||
|
||||
exports['test that the `sourcesContent` field has the original sources'] = function (assert, util) {
|
||||
var map = new SourceMapConsumer(util.testMapWithSourcesContent);
|
||||
var sourcesContent = map.sourcesContent;
|
||||
@@ -276,6 +442,26 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
|
||||
}, Error);
|
||||
};
|
||||
|
||||
exports['test that we can get the original source content for the sources on an indexed source map'] = function (assert, util) {
|
||||
var map = new SourceMapConsumer(util.indexedTestMap);
|
||||
var sources = map.sources;
|
||||
|
||||
assert.equal(map.sourceContentFor(sources[0]), ' ONE.foo = function (bar) {\n return baz(bar);\n };');
|
||||
assert.equal(map.sourceContentFor(sources[1]), ' TWO.inc = function (n) {\n return n + 1;\n };');
|
||||
assert.equal(map.sourceContentFor("one.js"), ' ONE.foo = function (bar) {\n return baz(bar);\n };');
|
||||
assert.equal(map.sourceContentFor("two.js"), ' TWO.inc = function (n) {\n return n + 1;\n };');
|
||||
assert.throws(function () {
|
||||
map.sourceContentFor("");
|
||||
}, Error);
|
||||
assert.throws(function () {
|
||||
map.sourceContentFor("/the/root/three.js");
|
||||
}, Error);
|
||||
assert.throws(function () {
|
||||
map.sourceContentFor("three.js");
|
||||
}, Error);
|
||||
};
|
||||
|
||||
|
||||
exports['test sourceRoot + generatedPositionFor'] = function (assert, util) {
|
||||
var map = new SourceMapGenerator({
|
||||
sourceRoot: 'foo/bar',
|
||||
@@ -314,7 +500,7 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
|
||||
assert.equal(pos.column, 2);
|
||||
};
|
||||
|
||||
exports['test allGeneratedPositionsFor'] = function (assert, util) {
|
||||
exports['test allGeneratedPositionsFor for line'] = function (assert, util) {
|
||||
var map = new SourceMapGenerator({
|
||||
file: 'generated.js'
|
||||
});
|
||||
@@ -357,7 +543,7 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
|
||||
assert.equal(mappings[1].column, 3);
|
||||
};
|
||||
|
||||
exports['test allGeneratedPositionsFor for line with no mappings'] = function (assert, util) {
|
||||
exports['test allGeneratedPositionsFor for line fuzzy'] = function (assert, util) {
|
||||
var map = new SourceMapGenerator({
|
||||
file: 'generated.js'
|
||||
});
|
||||
@@ -383,10 +569,12 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
|
||||
source: 'bar.coffee'
|
||||
});
|
||||
|
||||
assert.equal(mappings.length, 0);
|
||||
assert.equal(mappings.length, 1);
|
||||
assert.equal(mappings[0].line, 4);
|
||||
assert.equal(mappings[0].column, 2);
|
||||
};
|
||||
|
||||
exports['test allGeneratedPositionsFor source map with no mappings'] = function (assert, util) {
|
||||
exports['test allGeneratedPositionsFor for empty source map'] = function (assert, util) {
|
||||
var map = new SourceMapGenerator({
|
||||
file: 'generated.js'
|
||||
});
|
||||
@@ -400,6 +588,64 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
|
||||
assert.equal(mappings.length, 0);
|
||||
};
|
||||
|
||||
exports['test allGeneratedPositionsFor for column'] = function (assert, util) {
|
||||
var map = new SourceMapGenerator({
|
||||
file: 'generated.js'
|
||||
});
|
||||
map.addMapping({
|
||||
original: { line: 1, column: 1 },
|
||||
generated: { line: 1, column: 2 },
|
||||
source: 'foo.coffee'
|
||||
});
|
||||
map.addMapping({
|
||||
original: { line: 1, column: 1 },
|
||||
generated: { line: 1, column: 3 },
|
||||
source: 'foo.coffee'
|
||||
});
|
||||
map = new SourceMapConsumer(map.toString());
|
||||
|
||||
var mappings = map.allGeneratedPositionsFor({
|
||||
line: 1,
|
||||
column: 1,
|
||||
source: 'foo.coffee'
|
||||
});
|
||||
|
||||
assert.equal(mappings.length, 2);
|
||||
assert.equal(mappings[0].line, 1);
|
||||
assert.equal(mappings[0].column, 2);
|
||||
assert.equal(mappings[1].line, 1);
|
||||
assert.equal(mappings[1].column, 3);
|
||||
};
|
||||
|
||||
exports['test allGeneratedPositionsFor for column fuzzy'] = function (assert, util) {
|
||||
var map = new SourceMapGenerator({
|
||||
file: 'generated.js'
|
||||
});
|
||||
map.addMapping({
|
||||
original: { line: 1, column: 1 },
|
||||
generated: { line: 1, column: 2 },
|
||||
source: 'foo.coffee'
|
||||
});
|
||||
map.addMapping({
|
||||
original: { line: 1, column: 1 },
|
||||
generated: { line: 1, column: 3 },
|
||||
source: 'foo.coffee'
|
||||
});
|
||||
map = new SourceMapConsumer(map.toString());
|
||||
|
||||
var mappings = map.allGeneratedPositionsFor({
|
||||
line: 1,
|
||||
column: 0,
|
||||
source: 'foo.coffee'
|
||||
});
|
||||
|
||||
assert.equal(mappings.length, 2);
|
||||
assert.equal(mappings[0].line, 1);
|
||||
assert.equal(mappings[0].column, 2);
|
||||
assert.equal(mappings[1].line, 1);
|
||||
assert.equal(mappings[1].column, 3);
|
||||
};
|
||||
|
||||
exports['test computeColumnSpans'] = function (assert, util) {
|
||||
var map = new SourceMapGenerator({
|
||||
file: 'generated.js'
|
||||
@@ -544,6 +790,20 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
|
||||
'Source should be relative the host of the source root.');
|
||||
};
|
||||
|
||||
exports['test indexed source map errors when sections are out of order by line'] = function(assert, util) {
|
||||
// Make a deep copy of the indexedTestMap
|
||||
var misorderedIndexedTestMap = JSON.parse(JSON.stringify(util.indexedTestMap));
|
||||
|
||||
misorderedIndexedTestMap.sections[0].offset = {
|
||||
line: 2,
|
||||
column: 0
|
||||
};
|
||||
|
||||
assert.throws(function() {
|
||||
new SourceMapConsumer(misorderedIndexedTestMap);
|
||||
}, Error);
|
||||
};
|
||||
|
||||
exports['test github issue #64'] = function (assert, util) {
|
||||
var map = new SourceMapConsumer({
|
||||
"version": 3,
|
||||
|
||||
@@ -103,6 +103,27 @@ define("test/source-map/test-source-map-generator", ["require", "exports", "modu
|
||||
});
|
||||
};
|
||||
|
||||
exports['test adding mappings with skipValidation'] = function (assert, util) {
|
||||
var map = new SourceMapGenerator({
|
||||
file: 'generated-foo.js',
|
||||
sourceRoot: '.',
|
||||
skipValidation: true
|
||||
});
|
||||
|
||||
// Not enough info, caught by `util.getArgs`
|
||||
assert.throws(function () {
|
||||
map.addMapping({});
|
||||
});
|
||||
|
||||
// Original file position, but no source. Not checked.
|
||||
assert.doesNotThrow(function () {
|
||||
map.addMapping({
|
||||
generated: { line: 1, column: 1 },
|
||||
original: { line: 1, column: 1 }
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports['test that the correct mappings are being generated'] = function (assert, util) {
|
||||
var map = new SourceMapGenerator({
|
||||
file: 'min.js',
|
||||
@@ -660,6 +681,48 @@ define("test/source-map/test-source-map-generator", ["require", "exports", "modu
|
||||
});
|
||||
};
|
||||
|
||||
exports['test applySourceMap with unexact match'] = function (assert, util) {
|
||||
var map1 = new SourceMapGenerator({
|
||||
file: 'bundled-source'
|
||||
});
|
||||
map1.addMapping({
|
||||
generated: { line: 1, column: 4 },
|
||||
original: { line: 1, column: 4 },
|
||||
source: 'transformed-source'
|
||||
});
|
||||
map1.addMapping({
|
||||
generated: { line: 2, column: 4 },
|
||||
original: { line: 2, column: 4 },
|
||||
source: 'transformed-source'
|
||||
});
|
||||
|
||||
var map2 = new SourceMapGenerator({
|
||||
file: 'transformed-source'
|
||||
});
|
||||
map2.addMapping({
|
||||
generated: { line: 2, column: 0 },
|
||||
original: { line: 1, column: 0 },
|
||||
source: 'original-source'
|
||||
});
|
||||
|
||||
var expectedMap = new SourceMapGenerator({
|
||||
file: 'bundled-source'
|
||||
});
|
||||
expectedMap.addMapping({
|
||||
generated: { line: 1, column: 4 },
|
||||
original: { line: 1, column: 4 },
|
||||
source: 'transformed-source'
|
||||
});
|
||||
expectedMap.addMapping({
|
||||
generated: { line: 2, column: 4 },
|
||||
original: { line: 1, column: 0 },
|
||||
source: 'original-source'
|
||||
});
|
||||
|
||||
map1.applySourceMap(new SourceMapConsumer(map2.toJSON()));
|
||||
|
||||
util.assertEqualMaps(assert, map1.toJSON(), expectedMap.toJSON());
|
||||
};
|
||||
});
|
||||
function run_test() {
|
||||
runSourceMapTests('test/source-map/test-source-map-generator', do_throw);
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* 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/. */
|
||||
|
||||
const TEST_URI = "data:text/html;charset=utf-8,<p>bug 585991 - autocomplete popup keyboard usage test";
|
||||
let HUD, popup, jsterm, inputNode, completeNode;
|
||||
|
||||
let test = asyncTest(function*() {
|
||||
yield loadTab(TEST_URI);
|
||||
let hud = yield openConsole();
|
||||
|
||||
yield consoleOpened(hud);
|
||||
yield popupHideAfterTab();
|
||||
yield testReturnKey();
|
||||
yield dontShowArrayNumbers();
|
||||
yield testReturnWithNoSelection();
|
||||
yield popupHideAfterReturnWithNoSelection();
|
||||
yield testCompletionInText();
|
||||
yield popupHideAfterCompletionInText();
|
||||
|
||||
HUD = popup = jsterm = inputNode = completeNode = null;
|
||||
});
|
||||
|
||||
let consoleOpened = Task.async(function*(aHud) {
|
||||
let deferred = promise.defer();
|
||||
HUD = aHud;
|
||||
info("web console opened");
|
||||
|
||||
jsterm = HUD.jsterm;
|
||||
|
||||
yield jsterm.execute("window.foobarBug585991={" +
|
||||
"'item0': 'value0'," +
|
||||
"'item1': 'value1'," +
|
||||
"'item2': 'value2'," +
|
||||
"'item3': 'value3'" +
|
||||
"}");
|
||||
yield jsterm.execute("window.testBug873250a = 'hello world';"
|
||||
+ "window.testBug873250b = 'hello world 2';");
|
||||
popup = jsterm.autocompletePopup;
|
||||
completeNode = jsterm.completeNode;
|
||||
inputNode = jsterm.inputNode;
|
||||
|
||||
ok(!popup.isOpen, "popup is not open");
|
||||
|
||||
popup._panel.addEventListener("popupshown", function onShown() {
|
||||
popup._panel.removeEventListener("popupshown", onShown, false);
|
||||
|
||||
ok(popup.isOpen, "popup is open");
|
||||
|
||||
// 4 values, and the following properties:
|
||||
// __defineGetter__ __defineSetter__ __lookupGetter__ __lookupSetter__
|
||||
// __proto__ hasOwnProperty isPrototypeOf propertyIsEnumerable
|
||||
// toLocaleString toString toSource unwatch valueOf watch constructor.
|
||||
is(popup.itemCount, 19, "popup.itemCount is correct");
|
||||
|
||||
let sameItems = popup.getItems().reverse().map(function(e) {return e.label;});
|
||||
ok(sameItems.every(function(prop, index) {
|
||||
return [
|
||||
"__defineGetter__",
|
||||
"__defineSetter__",
|
||||
"__lookupGetter__",
|
||||
"__lookupSetter__",
|
||||
"__proto__",
|
||||
"constructor",
|
||||
"hasOwnProperty",
|
||||
"isPrototypeOf",
|
||||
"item0",
|
||||
"item1",
|
||||
"item2",
|
||||
"item3",
|
||||
"propertyIsEnumerable",
|
||||
"toLocaleString",
|
||||
"toSource",
|
||||
"toString",
|
||||
"unwatch",
|
||||
"valueOf",
|
||||
"watch",
|
||||
][index] === prop}), "getItems returns the items we expect");
|
||||
|
||||
is(popup.selectedIndex, 18,
|
||||
"Index of the first item from bottom is selected.");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
|
||||
let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");
|
||||
|
||||
is(popup.selectedIndex, 0, "index 0 is selected");
|
||||
is(popup.selectedItem.label, "watch", "watch is selected");
|
||||
is(completeNode.value, prefix + "watch",
|
||||
"completeNode.value holds watch");
|
||||
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
|
||||
is(popup.selectedIndex, 1, "index 1 is selected");
|
||||
is(popup.selectedItem.label, "valueOf", "valueOf is selected");
|
||||
is(completeNode.value, prefix + "valueOf",
|
||||
"completeNode.value holds valueOf");
|
||||
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
|
||||
is(popup.selectedIndex, 0, "index 0 is selected");
|
||||
is(popup.selectedItem.label, "watch", "watch is selected");
|
||||
is(completeNode.value, prefix + "watch",
|
||||
"completeNode.value holds watch");
|
||||
|
||||
let currentSelectionIndex = popup.selectedIndex;
|
||||
|
||||
EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
|
||||
|
||||
ok(popup.selectedIndex > currentSelectionIndex,
|
||||
"Index is greater after PGDN");
|
||||
|
||||
currentSelectionIndex = popup.selectedIndex;
|
||||
EventUtils.synthesizeKey("VK_PAGE_UP", {});
|
||||
|
||||
ok(popup.selectedIndex < currentSelectionIndex, "Index is less after Page UP");
|
||||
|
||||
EventUtils.synthesizeKey("VK_END", {});
|
||||
is(popup.selectedIndex, 18, "index is last after End");
|
||||
|
||||
EventUtils.synthesizeKey("VK_HOME", {});
|
||||
is(popup.selectedIndex, 0, "index is first after Home");
|
||||
|
||||
info("press Tab and wait for popup to hide");
|
||||
popup._panel.addEventListener("popuphidden", function popupHidden() {
|
||||
popup._panel.removeEventListener("popuphidden", popupHidden, false);
|
||||
deferred.resolve();
|
||||
}, false);
|
||||
EventUtils.synthesizeKey("VK_TAB", {});
|
||||
}, false);
|
||||
|
||||
info("wait for completion: window.foobarBug585991.");
|
||||
jsterm.setInputValue("window.foobarBug585991");
|
||||
EventUtils.synthesizeKey(".", {});
|
||||
|
||||
return deferred.promise;
|
||||
});
|
||||
|
||||
function popupHideAfterTab()
|
||||
{
|
||||
let deferred = promise.defer();
|
||||
|
||||
// At this point the completion suggestion should be accepted.
|
||||
ok(!popup.isOpen, "popup is not open");
|
||||
|
||||
is(inputNode.value, "window.foobarBug585991.watch",
|
||||
"completion was successful after VK_TAB");
|
||||
|
||||
ok(!completeNode.value, "completeNode is empty");
|
||||
|
||||
popup._panel.addEventListener("popupshown", function onShown() {
|
||||
popup._panel.removeEventListener("popupshown", onShown, false);
|
||||
|
||||
ok(popup.isOpen, "popup is open");
|
||||
|
||||
is(popup.itemCount, 19, "popup.itemCount is correct");
|
||||
|
||||
is(popup.selectedIndex, 18, "First index from bottom is selected");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
|
||||
let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");
|
||||
|
||||
is(popup.selectedIndex, 0, "index 0 is selected");
|
||||
is(popup.selectedItem.label, "watch", "watch is selected");
|
||||
is(completeNode.value, prefix + "watch",
|
||||
"completeNode.value holds watch");
|
||||
|
||||
popup._panel.addEventListener("popuphidden", function onHidden() {
|
||||
popup._panel.removeEventListener("popuphidden", onHidden, false);
|
||||
|
||||
ok(!popup.isOpen, "popup is not open after VK_ESCAPE");
|
||||
|
||||
is(inputNode.value, "window.foobarBug585991.",
|
||||
"completion was cancelled");
|
||||
|
||||
ok(!completeNode.value, "completeNode is empty");
|
||||
|
||||
deferred.resolve();
|
||||
}, false);
|
||||
|
||||
info("press Escape to close the popup");
|
||||
executeSoon(function() {
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
});
|
||||
}, false);
|
||||
|
||||
info("wait for completion: window.foobarBug585991.");
|
||||
executeSoon(function() {
|
||||
jsterm.setInputValue("window.foobarBug585991");
|
||||
EventUtils.synthesizeKey(".", {});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function testReturnKey()
|
||||
{
|
||||
let deferred = promise.defer();
|
||||
|
||||
popup._panel.addEventListener("popupshown", function onShown() {
|
||||
popup._panel.removeEventListener("popupshown", onShown, false);
|
||||
|
||||
ok(popup.isOpen, "popup is open");
|
||||
|
||||
is(popup.itemCount, 19, "popup.itemCount is correct");
|
||||
|
||||
is(popup.selectedIndex, 18, "First index from bottom is selected");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
|
||||
let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");
|
||||
|
||||
is(popup.selectedIndex, 0, "index 0 is selected");
|
||||
is(popup.selectedItem.label, "watch", "watch is selected");
|
||||
is(completeNode.value, prefix + "watch",
|
||||
"completeNode.value holds watch");
|
||||
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
|
||||
is(popup.selectedIndex, 1, "index 1 is selected");
|
||||
is(popup.selectedItem.label, "valueOf", "valueOf is selected");
|
||||
is(completeNode.value, prefix + "valueOf",
|
||||
"completeNode.value holds valueOf");
|
||||
|
||||
popup._panel.addEventListener("popuphidden", function onHidden() {
|
||||
popup._panel.removeEventListener("popuphidden", onHidden, false);
|
||||
|
||||
ok(!popup.isOpen, "popup is not open after VK_RETURN");
|
||||
|
||||
is(inputNode.value, "window.foobarBug585991.valueOf",
|
||||
"completion was successful after VK_RETURN");
|
||||
|
||||
ok(!completeNode.value, "completeNode is empty");
|
||||
|
||||
deferred.resolve();
|
||||
}, false);
|
||||
|
||||
info("press Return to accept suggestion. wait for popup to hide");
|
||||
|
||||
executeSoon(() => EventUtils.synthesizeKey("VK_RETURN", {}));
|
||||
}, false);
|
||||
|
||||
info("wait for completion suggestions: window.foobarBug585991.");
|
||||
|
||||
executeSoon(function() {
|
||||
jsterm.setInputValue("window.foobarBug58599");
|
||||
EventUtils.synthesizeKey("1", {});
|
||||
EventUtils.synthesizeKey(".", {});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function dontShowArrayNumbers()
|
||||
{
|
||||
let deferred = promise.defer();
|
||||
|
||||
info("dontShowArrayNumbers");
|
||||
content.wrappedJSObject.foobarBug585991 = ["Sherlock Holmes"];
|
||||
|
||||
let jsterm = HUD.jsterm;
|
||||
let popup = jsterm.autocompletePopup;
|
||||
let completeNode = jsterm.completeNode;
|
||||
|
||||
popup._panel.addEventListener("popupshown", function onShown() {
|
||||
popup._panel.removeEventListener("popupshown", onShown, false);
|
||||
|
||||
let sameItems = popup.getItems().map(function(e) {return e.label;});
|
||||
ok(!sameItems.some(function(prop, index) { prop === "0"; }),
|
||||
"Completing on an array doesn't show numbers.");
|
||||
|
||||
popup._panel.addEventListener("popuphidden", function popupHidden() {
|
||||
popup._panel.removeEventListener("popuphidden", popupHidden, false);
|
||||
deferred.resolve();
|
||||
}, false);
|
||||
|
||||
info("wait for popup to hide");
|
||||
executeSoon(() => EventUtils.synthesizeKey("VK_ESCAPE", {}));
|
||||
}, false);
|
||||
|
||||
info("wait for popup to show");
|
||||
executeSoon(() => {
|
||||
jsterm.setInputValue("window.foobarBug585991");
|
||||
EventUtils.synthesizeKey(".", {});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function testReturnWithNoSelection()
|
||||
{
|
||||
let deferred = promise.defer();
|
||||
|
||||
info("test pressing return with open popup, but no selection, see bug 873250");
|
||||
|
||||
popup._panel.addEventListener("popupshown", function onShown() {
|
||||
popup._panel.removeEventListener("popupshown", onShown);
|
||||
|
||||
ok(popup.isOpen, "popup is open");
|
||||
is(popup.itemCount, 2, "popup.itemCount is correct");
|
||||
isnot(popup.selectedIndex, -1, "popup.selectedIndex is correct");
|
||||
|
||||
info("press Return and wait for popup to hide");
|
||||
popup._panel.addEventListener("popuphidden", function popupHidden() {
|
||||
popup._panel.removeEventListener("popuphidden", popupHidden);
|
||||
deferred.resolve();
|
||||
});
|
||||
executeSoon(() => EventUtils.synthesizeKey("VK_RETURN", {}));
|
||||
});
|
||||
|
||||
executeSoon(() => {
|
||||
info("wait for popup to show");
|
||||
jsterm.setInputValue("window.testBu");
|
||||
EventUtils.synthesizeKey("g", {});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function popupHideAfterReturnWithNoSelection()
|
||||
{
|
||||
ok(!popup.isOpen, "popup is not open after VK_RETURN");
|
||||
|
||||
is(inputNode.value, "", "inputNode is empty after VK_RETURN");
|
||||
is(completeNode.value, "", "completeNode is empty");
|
||||
is(jsterm.history[jsterm.history.length-1], "window.testBug",
|
||||
"jsterm history is correct");
|
||||
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
function testCompletionInText()
|
||||
{
|
||||
info("test that completion works inside text, see bug 812618");
|
||||
|
||||
let deferred = promise.defer();
|
||||
|
||||
popup._panel.addEventListener("popupshown", function onShown() {
|
||||
popup._panel.removeEventListener("popupshown", onShown);
|
||||
|
||||
ok(popup.isOpen, "popup is open");
|
||||
is(popup.itemCount, 2, "popup.itemCount is correct");
|
||||
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is(popup.selectedIndex, 0, "popup.selectedIndex is correct");
|
||||
ok(!completeNode.value, "completeNode.value is empty");
|
||||
|
||||
let items = popup.getItems().reverse().map(e => e.label);
|
||||
let sameItems = items.every((prop, index) =>
|
||||
["testBug873250a", "testBug873250b"][index] === prop);
|
||||
ok(sameItems, "getItems returns the items we expect");
|
||||
|
||||
info("press Tab and wait for popup to hide");
|
||||
popup._panel.addEventListener("popuphidden", function popupHidden() {
|
||||
popup._panel.removeEventListener("popuphidden", popupHidden);
|
||||
deferred.resolve();
|
||||
});
|
||||
EventUtils.synthesizeKey("VK_TAB", {});
|
||||
});
|
||||
|
||||
jsterm.setInputValue("dump(window.testBu)");
|
||||
inputNode.selectionStart = inputNode.selectionEnd = 18;
|
||||
EventUtils.synthesizeKey("g", {});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function popupHideAfterCompletionInText()
|
||||
{
|
||||
// At this point the completion suggestion should be accepted.
|
||||
ok(!popup.isOpen, "popup is not open");
|
||||
is(inputNode.value, "dump(window.testBug873250b)",
|
||||
"completion was successful after VK_TAB");
|
||||
is(inputNode.selectionStart, 26, "cursor location is correct");
|
||||
is(inputNode.selectionStart, inputNode.selectionEnd, "cursor location (confirmed)");
|
||||
ok(!completeNode.value, "completeNode is empty");
|
||||
|
||||
return promise.resolve();
|
||||
}
|
||||
@@ -45,6 +45,16 @@
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.dbg-breakpoint-condition-thrown-message {
|
||||
display: none;
|
||||
color: var(--theme-highlight-red);
|
||||
}
|
||||
|
||||
.dbg-breakpoint.dbg-breakpoint-condition-thrown .dbg-breakpoint-condition-thrown-message {
|
||||
display: block;
|
||||
-moz-padding-start: 0;
|
||||
}
|
||||
|
||||
/* Sources toolbar */
|
||||
|
||||
#sources-toolbar > .devtools-toolbarbutton,
|
||||
|
||||
Reference in New Issue
Block a user