merge m-c to fx-team

This commit is contained in:
Tim Taubert 2013-11-01 10:47:21 +01:00
commit 2090ace474
53 changed files with 1044 additions and 198 deletions

View File

@ -978,7 +978,7 @@ var WebappsHelper = {
});
break;
case "webapps-close":
shell.sendEvent(shell.getContentWindow(), "webapps-close",
shell.sendEvent(getContentWindow(), "webapps-close",
{
__exposedProps__: { "manifestURL": "r" },
"manifestURL": json.manifestURL

View File

@ -4206,6 +4206,10 @@ var TabsProgressListener = {
if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)
return;
// Filter out location changes in sub documents.
if (!aWebProgress.isTopLevel)
return;
// Only need to call locationChange if the PopupNotifications object
// for this window has already been initialized (i.e. its getter no
// longer exists)
@ -4214,10 +4218,7 @@ var TabsProgressListener = {
gBrowser.getNotificationBox(aBrowser).removeTransientNotifications();
// Filter out location changes in sub documents.
if (aWebProgress.isTopLevel) {
FullZoom.onLocationChange(aLocationURI, false, aBrowser);
}
FullZoom.onLocationChange(aLocationURI, false, aBrowser);
},
onRefreshAttempted: function (aBrowser, aWebProgress, aURI, aDelay, aSameURI) {

View File

@ -31,6 +31,15 @@ let gPage = {
this._updateAttributes(enabled);
},
/**
* True if the page is allowed to capture thumbnails using the background
* thumbnail service.
*/
get allowBackgroundCaptures() {
return document.documentElement.getAttribute("allow-background-captures") ==
"true";
},
/**
* Listens for notifications specific to this page.
*/
@ -74,6 +83,20 @@ let gPage = {
this._initialized = true;
this._mutationObserver = new MutationObserver(() => {
if (this.allowBackgroundCaptures) {
for (let site of gGrid.sites) {
if (site) {
site.captureIfMissing();
}
}
}
});
this._mutationObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ["allow-background-captures"],
});
gLinks.populateCache(function () {
// Initialize and render the grid.
gGrid.init();
@ -123,6 +146,7 @@ let gPage = {
handleEvent: function Page_handleEvent(aEvent) {
switch (aEvent.type) {
case "unload":
this._mutationObserver.disconnect();
gAllPages.unregister(this);
break;
case "click":

View File

@ -0,0 +1,31 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function () { // bug 673569 workaround :(
const ALLOW_BG_CAPTURES_MSG = "BrowserNewTabPreloader:allowBackgroundCaptures";
addMessageListener(ALLOW_BG_CAPTURES_MSG, function onMsg(msg) {
removeMessageListener(ALLOW_BG_CAPTURES_MSG, onMsg);
if (content.document.readyState == "complete") {
setAllowBackgroundCaptures();
return;
}
// This is the case when preloading is disabled.
addEventListener("load", function onLoad(event) {
if (event.target == content.document) {
removeEventListener("load", onLoad, true);
setAllowBackgroundCaptures();
}
}, true);
});
function setAllowBackgroundCaptures() {
content.document.documentElement.setAttribute("allow-background-captures",
"true");
}
})();

View File

@ -133,11 +133,20 @@ Site.prototype = {
this._updateAttributes(true);
// Capture the page if the thumbnail is missing, which will cause page.js
// to be notified and call our refreshThumbnail() method.
BackgroundPageThumbs.captureIfMissing(this.url);
this.captureIfMissing();
// but still display whatever thumbnail might be available now.
this.refreshThumbnail();
},
/**
* Captures the site's thumbnail in the background, but only if there's no
* existing thumbnail and the page allows background captures.
*/
captureIfMissing: function Site_captureIfMissing() {
if (gPage.allowBackgroundCaptures)
BackgroundPageThumbs.captureIfMissing(this.url);
},
/**
* Refreshes the thumbnail for the site.
*/

View File

@ -829,23 +829,41 @@ var tests = [
});
}
},
{ // Test #28 - location change in embedded frame removes notification
{ // Test #28 - location change in an embedded frame should not remove a notification
run: function () {
loadURI("data:text/html,<iframe id='iframe' src='http://example.com/'>", function () {
let notifyObj = new basicNotification();
notifyObj.options.eventCallback = function (eventName) {
loadURI("data:text/html;charset=utf8,<iframe id='iframe' src='http://example.com/'>", function () {
this.notifyObj = new basicNotification();
this.notifyObj.options.eventCallback = function (eventName) {
if (eventName == "removed") {
ok(true, "Notification removed in background tab after reloading");
executeSoon(goNext);
ok(false, "Test 28: Notification removed from browser when subframe navigated");
}
};
showNotification(notifyObj);
executeSoon(function () {
content.document.getElementById("iframe")
.setAttribute("src", "http://example.org/");
});
});
}
showNotification(this.notifyObj);
}.bind(this));
},
onShown: function (popup) {
let self = this;
let progressListener = {
onLocationChange: function onLocationChange(aBrowser) {
if (aBrowser != gBrowser.selectedBrowser) {
return;
}
let notification = PopupNotifications.getNotification(self.notifyObj.id,
self.notifyObj.browser);
ok(notification != null, "Test 28: Notification remained when subframe navigated");
self.notifyObj.options.eventCallback = undefined;
notification.remove();
gBrowser.removeTabsProgressListener(progressListener);
},
};
info("Test 28: Adding progress listener and performing navigation");
gBrowser.addTabsProgressListener(progressListener);
content.document.getElementById("iframe")
.setAttribute("src", "http://example.org/");
},
onHidden: function () {}
},
{ // Test #29 - Popup Notifications should catch exceptions from callbacks
run: function () {

View File

@ -1,6 +1,7 @@
[DEFAULT]
support-files = head.js
[browser_newtab_background_captures.js]
[browser_newtab_block.js]
[browser_newtab_bug721442.js]
[browser_newtab_bug722273.js]

View File

@ -0,0 +1,100 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Verifies that hidden, pre-loaded newtabs don't allow background captures, and
* when unhidden, do allow background captures.
*/
const CAPTURE_PREF = "browser.pagethumbnails.capturing_disabled";
function runTests() {
let imports = {};
Cu.import("resource://gre/modules/PageThumbs.jsm", imports);
Cu.import("resource:///modules/BrowserNewTabPreloader.jsm", imports);
// Disable captures.
let originalDisabledState = Services.prefs.getBoolPref(CAPTURE_PREF);
Services.prefs.setBoolPref(CAPTURE_PREF, true);
// Make sure the thumbnail doesn't exist yet.
let siteName = "newtab_background_captures";
let url = "http://example.com/#" + siteName;
let path = imports.PageThumbsStorage.getFilePathForURL(url);
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(path);
try {
file.remove(false);
}
catch (err) {}
// Add a top site.
yield setLinks(siteName);
// We need a handle to a hidden, pre-loaded newtab so we can verify that it
// doesn't allow background captures. Add a newtab, which triggers creation
// of a hidden newtab, and then keep calling BrowserNewTabPreloader.newTab
// until it returns true, meaning that it swapped the passed-in tab's docshell
// for the hidden newtab docshell.
let tab = gWindow.gBrowser.addTab("about:blank");
yield addNewTabPageTab();
let swapWaitCount = 0;
let swapped = imports.BrowserNewTabPreloader.newTab(tab);
while (!swapped) {
if (++swapWaitCount == 10) {
ok(false, "Timed out waiting for newtab docshell swap.");
return;
}
// Give the hidden newtab some time to finish loading.
yield wait(2000);
info("Checking newtab swap " + swapWaitCount);
swapped = imports.BrowserNewTabPreloader.newTab(tab);
}
// The tab's docshell is now the previously hidden newtab docshell.
let doc = tab.linkedBrowser.contentDocument;
isnot(doc.documentElement.getAttribute("allow-background-captures"), "true",
"Pre-loaded docshell just synchronously swapped, so background " +
"captures should not be allowed yet");
// Enable captures.
Services.prefs.setBoolPref(CAPTURE_PREF, false);
// Now that the newtab is visible, its allow-background-captures attribute
// should be set eventually.
let allowBackgroundCaptures = false;
let mutationObserver = new MutationObserver(() => {
mutationObserver.disconnect();
allowBackgroundCaptures = true;
is(doc.documentElement.getAttribute("allow-background-captures"), "true",
"allow-background-captures should now be true");
info("Waiting for thumbnail to be created after observing " +
"allow-background-captures change");
});
mutationObserver.observe(doc.documentElement, {
attributes: true,
attributeFilter: ["allow-background-captures"],
});
// And the allow-background-captures change should trigger the thumbnail
// capture.
Services.obs.addObserver(function onCreate(subj, topic, data) {
if (data != url)
return;
ok(allowBackgroundCaptures,
"page-thumbnail:create should be observed after " +
"allow-background-captures was set");
Services.obs.removeObserver(onCreate, "page-thumbnail:create");
// Test finished!
Services.prefs.setBoolPref(CAPTURE_PREF, originalDisabledState);
file.remove(false);
TestRunner.next();
}, "page-thumbnail:create", false);
info("Waiting for allow-background-captures change");
yield true;
}
function wait(ms) {
setTimeout(TestRunner.next, ms);
}

View File

@ -64,6 +64,7 @@ browser.jar:
content/browser/newtab/newTab.xul (content/newtab/newTab.xul)
* content/browser/newtab/newTab.js (content/newtab/newTab.js)
content/browser/newtab/newTab.css (content/newtab/newTab.css)
content/browser/newtab/preloaderContent.js (content/newtab/preloaderContent.js)
* content/browser/pageinfo/pageInfo.xul (content/pageinfo/pageInfo.xul)
content/browser/pageinfo/pageInfo.js (content/pageinfo/pageInfo.js)
content/browser/pageinfo/pageInfo.css (content/pageinfo/pageInfo.css)

View File

@ -49,7 +49,7 @@
<button id="requests-menu-status-button"
class="requests-menu-header-button requests-menu-status"
onclick="NetMonitorView.RequestsMenu.sortBy('status')"
label="&netmonitorUI.toolbar.status;">
label="&netmonitorUI.toolbar.status2;">
</button>
<button id="requests-menu-method-button"
class="requests-menu-header-button requests-menu-method"

View File

@ -19,8 +19,13 @@ const Editor = require("devtools/sourceeditor/editor");
// The panel's window global is an EventEmitter firing the following events:
const EVENTS = {
// When new programs are received from the server.
NEW_PROGRAM: "ShaderEditor:NewProgram",
PROGRAMS_ADDED: "ShaderEditor:ProgramsAdded",
// When the vertex and fragment sources were shown in the editor.
SOURCES_SHOWN: "ShaderEditor:SourcesShown",
// When a shader's source was edited and compiled via the editor.
SHADER_COMPILED: "ShaderEditor:ShaderCompiled"
};
@ -72,10 +77,12 @@ let EventsHandler = {
*/
initialize: function() {
this._onHostChanged = this._onHostChanged.bind(this);
this._onWillNavigate = this._onWillNavigate.bind(this);
this._onTabNavigated = this._onTabNavigated.bind(this);
this._onProgramLinked = this._onProgramLinked.bind(this);
this._onProgramsAdded = this._onProgramsAdded.bind(this);
gToolbox.on("host-changed", this._onHostChanged);
gTarget.on("will-navigate", this._onWillNavigate);
gTarget.on("will-navigate", this._onTabNavigated);
gTarget.on("navigate", this._onTabNavigated);
gFront.on("program-linked", this._onProgramLinked);
},
@ -85,7 +92,8 @@ let EventsHandler = {
*/
destroy: function() {
gToolbox.off("host-changed", this._onHostChanged);
gTarget.off("will-navigate", this._onWillNavigate);
gTarget.off("will-navigate", this._onTabNavigated);
gTarget.off("navigate", this._onTabNavigated);
gFront.off("program-linked", this._onProgramLinked);
},
@ -101,20 +109,51 @@ let EventsHandler = {
/**
* Called for each location change in the debugged tab.
*/
_onWillNavigate: function() {
gFront.setup();
_onTabNavigated: function(event) {
switch (event) {
case "will-navigate": {
// Make sure the backend is prepared to handle WebGL contexts.
gFront.setup({ reload: false });
ShadersListView.empty();
ShadersEditorsView.setText({ vs: "", fs: "" });
$("#reload-notice").hidden = true;
$("#waiting-notice").hidden = false;
$("#content").hidden = true;
// Reset UI.
ShadersListView.empty();
ShadersEditorsView.setText({ vs: "", fs: "" });
$("#reload-notice").hidden = true;
$("#waiting-notice").hidden = false;
$("#content").hidden = true;
break;
}
case "navigate": {
// Manually retrieve the list of program actors known to the server,
// because the backend won't emit "program-linked" notifications
// in the case of a bfcache navigation (since no new programs are
// actually linked).
gFront.getPrograms().then(this._onProgramsAdded);
break;
}
}
},
/**
* Called every time a program was linked in the debugged tab.
*/
_onProgramLinked: function(programActor) {
this._addProgram(programActor);
window.emit(EVENTS.NEW_PROGRAM);
},
/**
* Callback for the front's getPrograms() method.
*/
_onProgramsAdded: function(programActors) {
programActors.forEach(this._addProgram);
window.emit(EVENTS.PROGRAMS_ADDED);
},
/**
* Adds a program to the shaders list and unhides any modal notices.
*/
_addProgram: function(programActor) {
$("#waiting-notice").hidden = true;
$("#reload-notice").hidden = true;
$("#content").hidden = false;
@ -163,6 +202,10 @@ let ShadersListView = Heritage.extend(WidgetMethods, {
* The program actor coming from the active thread.
*/
addProgram: function(programActor) {
if (this.hasProgram(programActor)) {
return;
}
// Currently, there's no good way of differentiating between programs
// in a way that helps humans. It will be a good idea to implement a
// standard of allowing debuggees to add some identifiable metadata to their
@ -192,6 +235,18 @@ let ShadersListView = Heritage.extend(WidgetMethods, {
}
},
/**
* Returns whether a program was already added to this programs container.
*
* @param object programActor
* The program actor coming from the active thread.
* @param boolean
* True if the program was added, false otherwise.
*/
hasProgram: function(programActor) {
return !!this.attachments.filter(e => e.programActor == programActor).length;
},
/**
* The select listener for the sources container.
*/

View File

@ -24,7 +24,7 @@
<button id="requests-menu-reload-notice-button"
class="devtools-toolbarbutton"
label="&shaderEditorUI.reloadNotice1;"
oncommand="gFront.setup();"/>
oncommand="gFront.setup({ reload: true });"/>
<label id="requests-menu-reload-notice-label"
class="plain"
value="&shaderEditorUI.reloadNotice2;"/>

View File

@ -6,6 +6,7 @@ support-files =
head.js
[browser_se_aaa_run_first_leaktest.js]
[browser_se_bfcache.js]
[browser_se_editors-contents.js]
[browser_se_editors-lazy-init.js]
[browser_se_first-run.js]
@ -31,3 +32,5 @@ support-files =
[browser_webgl-actor-test-12.js]
[browser_webgl-actor-test-13.js]
[browser_webgl-actor-test-14.js]
[browser_webgl-actor-test-15.js]
[browser_webgl-actor-test-16.js]

View File

@ -0,0 +1,64 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the shader editor works with bfcache.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(SIMPLE_CANVAS_URL);
let { gFront, $, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
let reloaded = reload(target);
let firstProgram = yield once(gFront, "program-linked");
yield reloaded;
let navigated = navigate(target, MULTIPLE_CONTEXTS_URL);
let secondProgram = yield once(gFront, "program-linked");
let thirdProgram = yield once(gFront, "program-linked");
yield navigated;
let vsEditor = yield ShadersEditorsView._getEditor("vs");
let fsEditor = yield ShadersEditorsView._getEditor("fs");
yield navigateInHistory(target, "back", "will-navigate");
yield once(panel.panelWin, EVENTS.PROGRAMS_ADDED);
yield once(panel.panelWin, EVENTS.SOURCES_SHOWN);
is($("#content").hidden, false,
"The tool's content should not be hidden.");
is(ShadersListView.itemCount, 1,
"The shaders list contains one entry after navigating back.");
is(ShadersListView.selectedIndex, 0,
"The shaders list has a correct selection after navigating back.");
is(vsEditor.getText().indexOf("gl_Position"), 170,
"The vertex shader editor contains the correct text.");
is(fsEditor.getText().indexOf("gl_FragColor"), 97,
"The fragment shader editor contains the correct text.");
yield navigateInHistory(target, "forward", "will-navigate");
yield once(panel.panelWin, EVENTS.PROGRAMS_ADDED);
yield once(panel.panelWin, EVENTS.SOURCES_SHOWN);
is($("#content").hidden, false,
"The tool's content should not be hidden.");
is(ShadersListView.itemCount, 2,
"The shaders list contains two entries after navigating forward.");
is(ShadersListView.selectedIndex, 0,
"The shaders list has a correct selection after navigating forward.");
is(vsEditor.getText().indexOf("gl_Position"), 100,
"The vertex shader editor contains the correct text.");
is(fsEditor.getText().indexOf("gl_FragColor"), 89,
"The fragment shader editor contains the correct text.");
yield teardown(panel);
finish();
}
function once(aTarget, aEvent) {
let deferred = promise.defer();
aTarget.once(aEvent, deferred.resolve);
return deferred.promise;
}

View File

@ -12,7 +12,7 @@ function ifWebGLSupported() {
let navigated = once(target, "navigate");
let linked = once(front, "program-linked");
yield front.setup();
yield front.setup({ reload: true });
ok(true, "The front was setup up successfully.");
yield navigated;

View File

@ -8,7 +8,7 @@
function ifWebGLSupported() {
let [target, debuggee, front] = yield initBackend(SIMPLE_CANVAS_URL);
front.setup();
front.setup({ reload: true });
let programActor = yield once(front, "program-linked");
ok(programActor,

View File

@ -8,7 +8,7 @@
function ifWebGLSupported() {
let [target, debuggee, front] = yield initBackend(SIMPLE_CANVAS_URL);
front.setup();
front.setup({ reload: true });
let programActor = yield once(front, "program-linked");
let vertexShader = yield programActor.getVertexShader();

View File

@ -8,7 +8,7 @@
function ifWebGLSupported() {
let [target, debuggee, front] = yield initBackend(SIMPLE_CANVAS_URL);
front.setup();
front.setup({ reload: true });
let programActor = yield once(front, "program-linked");
let vertexShader = yield programActor.getVertexShader();

View File

@ -7,7 +7,7 @@
function ifWebGLSupported() {
let [target, debuggee, front] = yield initBackend(SIMPLE_CANVAS_URL);
front.setup();
front.setup({ reload: true });
let programActor = yield once(front, "program-linked");
let vertexShader = yield programActor.getVertexShader();

View File

@ -8,7 +8,7 @@
function ifWebGLSupported() {
let [target, debuggee, front] = yield initBackend(SIMPLE_CANVAS_URL);
front.setup();
front.setup({ reload: true });
let programActor = yield once(front, "program-linked");
let vertexShader = yield programActor.getVertexShader();

View File

@ -8,7 +8,7 @@
function ifWebGLSupported() {
let [target, debuggee, front] = yield initBackend(SIMPLE_CANVAS_URL);
front.setup();
front.setup({ reload: true });
let programActor = yield once(front, "program-linked");
let vertexShader = yield programActor.getVertexShader();

View File

@ -9,36 +9,33 @@
function ifWebGLSupported() {
let [target, debuggee, front] = yield initBackend(SIMPLE_CANVAS_URL);
let linked = once(front, "program-linked");
yield front.setup();
yield linked;
front.setup({ reload: true });
yield testHighlighting((yield once(front, "program-linked")));
ok(true, "Canvas was correctly instrumented on the first navigation.");
let linked = once(front, "program-linked");
yield reload(target);
yield linked;
reload(target);
yield testHighlighting((yield once(front, "program-linked")));
ok(true, "Canvas was correctly instrumented on the second navigation.");
let linked = once(front, "program-linked");
yield reload(target);
yield linked;
reload(target);
yield testHighlighting((yield once(front, "program-linked")));
ok(true, "Canvas was correctly instrumented on the third navigation.");
let programActor = yield linked;
let vertexShader = yield programActor.getVertexShader();
let fragmentShader = yield programActor.getFragmentShader();
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
ok(true, "The top left pixel color was correct before highlighting.");
yield programActor.highlight([0, 0, 1, 1]);
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true);
ok(true, "The top left pixel color is correct after highlighting.");
yield programActor.unhighlight();
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
ok(true, "The top left pixel color is correct after unhighlighting.");
yield removeTab(target.tab);
finish();
function testHighlighting(programActor) {
return Task.spawn(function() {
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
ok(true, "The top left pixel color was correct before highlighting.");
yield programActor.highlight([0, 0, 1, 1]);
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true);
ok(true, "The top left pixel color is correct after highlighting.");
yield programActor.unhighlight();
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
ok(true, "The top left pixel color is correct after unhighlighting.");
});
}
}

View File

@ -10,7 +10,7 @@ function ifWebGLSupported() {
let [target, debuggee, front] = yield initBackend(SIMPLE_CANVAS_URL);
let linked = once(front, "program-linked");
yield front.setup();
front.setup({ reload: true });
yield linked;
ok(true, "Canvas was correctly instrumented on the first navigation.");

View File

@ -8,7 +8,7 @@
function ifWebGLSupported() {
let [target, debuggee, front] = yield initBackend(SHADER_ORDER_URL);
front.setup();
front.setup({ reload: true });
let programActor = yield once(front, "program-linked");
let vertexShader = yield programActor.getVertexShader();

View File

@ -7,7 +7,7 @@
function ifWebGLSupported() {
let [target, debuggee, front] = yield initBackend(MULTIPLE_CONTEXTS_URL);
front.setup();
front.setup({ reload: true });
let firstProgramActor = yield once(front, "program-linked");
let secondProgramActor = yield once(front, "program-linked");

View File

@ -8,7 +8,7 @@
function ifWebGLSupported() {
let [target, debuggee, front] = yield initBackend(MULTIPLE_CONTEXTS_URL);
front.setup();
front.setup({ reload: true });
let firstProgramActor = yield once(front, "program-linked");
let secondProgramActor = yield once(front, "program-linked");

View File

@ -0,0 +1,127 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if program actors are cached when navigating in the bfcache.
*/
function ifWebGLSupported() {
let [target, debuggee, front] = yield initBackend(SIMPLE_CANVAS_URL);
front.setup({ reload: false });
reload(target);
let firstProgram = yield once(front, "program-linked");
yield checkFirstCachedPrograms(firstProgram);
yield checkHighlightingInTheFirstPage(firstProgram);
ok(true, "The cached programs behave correctly before the navigation.");
navigate(target, MULTIPLE_CONTEXTS_URL);
let secondProgram = yield once(front, "program-linked");
let thirdProgram = yield once(front, "program-linked");
yield checkSecondCachedPrograms(firstProgram, [secondProgram, thirdProgram]);
yield checkHighlightingInTheSecondPage(secondProgram, thirdProgram);
ok(true, "The cached programs behave correctly after the navigation.");
once(front, "program-linked").then(() => {
ok(false, "Shouldn't have received any more program-linked notifications.");
});
yield navigateInHistory(target, "back");
yield checkFirstCachedPrograms(firstProgram);
yield checkHighlightingInTheFirstPage(firstProgram);
ok(true, "The cached programs behave correctly after navigating back.");
yield navigateInHistory(target, "forward");
yield checkSecondCachedPrograms(firstProgram, [secondProgram, thirdProgram]);
yield checkHighlightingInTheSecondPage(secondProgram, thirdProgram);
ok(true, "The cached programs behave correctly after navigating forward.");
yield navigateInHistory(target, "back");
yield checkFirstCachedPrograms(firstProgram);
yield checkHighlightingInTheFirstPage(firstProgram);
ok(true, "The cached programs behave correctly after navigating back again.");
yield navigateInHistory(target, "forward");
yield checkSecondCachedPrograms(firstProgram, [secondProgram, thirdProgram]);
yield checkHighlightingInTheSecondPage(secondProgram, thirdProgram);
ok(true, "The cached programs behave correctly after navigating forward again.");
yield removeTab(target.tab);
finish();
function checkFirstCachedPrograms(programActor) {
return Task.spawn(function() {
let programs = yield front.getPrograms();
is(programs.length, 1,
"There should be 1 cached program actor.");
is(programs[0], programActor,
"The cached program actor was the expected one.");
})
}
function checkSecondCachedPrograms(oldProgramActor, newProgramActors) {
return Task.spawn(function() {
let programs = yield front.getPrograms();
is(programs.length, 2,
"There should be 2 cached program actors after the navigation.");
is(programs[0], newProgramActors[0],
"The first cached program actor was the expected one after the navigation.");
is(programs[1], newProgramActors[1],
"The second cached program actor was the expected one after the navigation.");
isnot(newProgramActors[0], oldProgramActor,
"The old program actor is not equal to the new first program actor.");
isnot(newProgramActors[1], oldProgramActor,
"The old program actor is not equal to the new second program actor.");
});
}
function checkHighlightingInTheFirstPage(programActor) {
return Task.spawn(function() {
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
ok(true, "The top left pixel color was correct before highlighting.");
yield programActor.highlight([0, 0, 1, 1]);
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true);
ok(true, "The top left pixel color is correct after highlighting.");
yield programActor.unhighlight();
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
ok(true, "The top left pixel color is correct after unhighlighting.");
});
}
function checkHighlightingInTheSecondPage(firstProgramActor, secondProgramActor) {
return Task.spawn(function() {
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
ok(true, "The two canvases are correctly drawn before highlighting.");
yield firstProgramActor.highlight([1, 0, 0, 1]);
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
ok(true, "The first canvas was correctly filled after highlighting.");
yield secondProgramActor.highlight([0, 1, 0, 1]);
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
ok(true, "The second canvas was correctly filled after highlighting.");
yield firstProgramActor.unhighlight();
yield secondProgramActor.unhighlight();
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
ok(true, "The two canvases were correctly filled after unhighlighting.");
});
}
}

View File

@ -0,0 +1,105 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if program actors are invalidated from the cache when a window is
* removed from the bfcache.
*/
function ifWebGLSupported() {
let [target, debuggee, front] = yield initBackend(SIMPLE_CANVAS_URL);
front.setup({ reload: false });
reload(target);
let firstProgram = yield once(front, "program-linked");
navigate(target, MULTIPLE_CONTEXTS_URL);
let secondProgram = yield once(front, "program-linked");
let thirdProgram = yield once(front, "program-linked");
yield navigateInHistory(target, "back");
let globalDestroyed = observe("inner-window-destroyed");
let globalCreated = observe("content-document-global-created");
reload(target);
yield globalDestroyed;
let programs = yield front.getPrograms();
is(programs.length, 0,
"There should be no cached program actors yet.");
yield globalCreated;
let programs = yield front.getPrograms();
is(programs.length, 1,
"There should be 1 cached program actor now.");
yield checkHighlightingInTheFirstPage(programs[0]);
ok(true, "The cached programs behave correctly after navigating back and reloading.");
yield navigateInHistory(target, "forward");
let globalDestroyed = observe("inner-window-destroyed");
let globalCreated = observe("content-document-global-created");
reload(target);
yield globalDestroyed;
let programs = yield front.getPrograms();
is(programs.length, 0,
"There should be no cached program actors yet.");
yield globalCreated;
let programs = yield front.getPrograms();
is(programs.length, 2,
"There should be 2 cached program actors now.");
yield checkHighlightingInTheSecondPage(programs[0], programs[1]);
ok(true, "The cached programs behave correctly after navigating forward and reloading.");
yield removeTab(target.tab);
finish();
function checkHighlightingInTheFirstPage(programActor) {
return Task.spawn(function() {
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
ok(true, "The top left pixel color was correct before highlighting.");
yield programActor.highlight([0, 0, 1, 1]);
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true);
ok(true, "The top left pixel color is correct after highlighting.");
yield programActor.unhighlight();
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
ok(true, "The top left pixel color is correct after unhighlighting.");
});
}
function checkHighlightingInTheSecondPage(firstProgramActor, secondProgramActor) {
return Task.spawn(function() {
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
ok(true, "The two canvases are correctly drawn before highlighting.");
yield firstProgramActor.highlight([1, 0, 0, 1]);
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
ok(true, "The first canvas was correctly filled after highlighting.");
yield secondProgramActor.highlight([0, 1, 0, 1]);
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
ok(true, "The second canvas was correctly filled after highlighting.");
yield firstProgramActor.unhighlight();
yield secondProgramActor.unhighlight();
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
ok(true, "The two canvases were correctly filled after unhighlighting.");
});
}
}

View File

@ -133,6 +133,19 @@ function once(aTarget, aEventName, aUseCapture = false) {
return deferred.promise;
}
function observe(aNotificationName, aOwnsWeak = false) {
info("Waiting for observer notification: '" + aNotificationName + ".");
let deferred = promise.defer();
Services.obs.addObserver(function onNotification(...aArgs) {
Services.obs.removeObserver(onNotification, aNotificationName);
deferred.resolve.apply(deferred, aArgs);
}, aNotificationName, aOwnsWeak);
return deferred.promise;
}
function waitForFrame(aDebuggee) {
let deferred = promise.defer();
aDebuggee.requestAnimationFrame(deferred.resolve);
@ -193,16 +206,19 @@ function ensurePixelIs(aDebuggee, aPosition, aColor, aWaitFlag = false, aSelecto
return promise.reject(null);
}
function navigate(aTarget, aUrl) {
let navigated = once(aTarget, "navigate");
aTarget.client.activeTab.navigateTo(aUrl);
return navigated;
function navigateInHistory(aTarget, aDirection, aWaitForTargetEvent = "navigate") {
executeSoon(() => content.history[aDirection]());
return once(aTarget, aWaitForTargetEvent);
}
function reload(aTarget) {
let navigated = once(aTarget, "navigate");
function navigate(aTarget, aUrl, aWaitForTargetEvent = "navigate") {
executeSoon(() => aTarget.client.activeTab.navigateTo(aUrl));
return once(aTarget, aWaitForTargetEvent);
}
function reload(aTarget, aWaitForTargetEvent = "navigate") {
executeSoon(() => aTarget.client.activeTab.reload());
return navigated;
return once(aTarget, aWaitForTargetEvent);
}
function initBackend(aUrl) {

View File

@ -181,6 +181,10 @@ Tooltip.prototype = {
this.stopTogglingOnHover();
}
// If no targetNodeCb callback is provided, then we need to hide the tooltip
// on mouseleave since baseNode is the target node itself
this._hideOnMouseLeave = !targetNodeCb;
this._basedNode = baseNode;
this._showDelay = showDelay;
this._targetNodeCb = targetNodeCb || (() => true);
@ -221,7 +225,7 @@ Tooltip.prototype = {
},
_showOnHover: function(target) {
if (this._targetNodeCb && this._targetNodeCb(target, this)) {
if (this._targetNodeCb(target, this)) {
this.show(target);
this._lastHovered = target;
}
@ -230,6 +234,9 @@ Tooltip.prototype = {
_onBaseNodeMouseLeave: function() {
clearNamedTimeout(this.uid);
this._lastHovered = null;
if (this._hideOnMouseLeave) {
this.hide();
}
},
/**

View File

@ -49,8 +49,6 @@ let Svc = {};
XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
'@mozilla.org/mime;1', 'nsIMIMEService');
let profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
function getBoolPref(pref, def) {
try {
return Services.prefs.getBoolPref(pref);
@ -222,7 +220,10 @@ ChromeActions.prototype = {
});
},
addProfilerMarker: function (marker) {
profiler.AddMarker(marker);
if ('nsIProfiler' in Ci) {
let profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
profiler.AddMarker(marker);
}
},
getPluginParams: function getPluginParams() {
return JSON.stringify({

View File

@ -1 +1 @@
0.7.501
0.7.502

View File

@ -15,9 +15,9 @@
- in the network table when empty. -->
<!ENTITY netmonitorUI.emptyNotice2 "Perform a request or reload the page to see detailed information about network activity.">
<!-- LOCALIZATION NOTE (netmonitorUI.toolbar.status): This is the label displayed
<!-- LOCALIZATION NOTE (netmonitorUI.toolbar.status2): This is the label displayed
- in the network table toolbar, above the "status" column. -->
<!ENTITY netmonitorUI.toolbar.status "√">
<!ENTITY netmonitorUI.toolbar.status2 "✓">
<!-- LOCALIZATION NOTE (netmonitorUI.toolbar.method): This is the label displayed
- in the network table toolbar, above the "method" column. -->

View File

@ -35,6 +35,8 @@ const TOPIC_TIMER_CALLBACK = "timer-callback";
const TOPIC_DELAYED_STARTUP = "browser-delayed-startup-finished";
const TOPIC_XUL_WINDOW_CLOSED = "xul-window-destroyed";
const FRAME_SCRIPT_URL = "chrome://browser/content/newtab/preloaderContent.js";
function createTimer(obj, delay) {
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.init(obj, delay, Ci.nsITimer.TYPE_ONE_SHOT);
@ -61,6 +63,7 @@ this.BrowserNewTabPreloader = {
},
newTab: function Preloader_newTab(aTab) {
let swapped = false;
let win = aTab.ownerDocument.defaultView;
if (win.gBrowser) {
let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
@ -69,11 +72,17 @@ this.BrowserNewTabPreloader = {
let {width, height} = utils.getBoundsWithoutFlushing(win.gBrowser);
let hiddenBrowser = HiddenBrowsers.get(width, height)
if (hiddenBrowser) {
return hiddenBrowser.swapWithNewTab(aTab);
swapped = hiddenBrowser.swapWithNewTab(aTab);
}
// aTab's browser is now visible and is therefore allowed to make
// background captures.
let msgMan = aTab.linkedBrowser.messageManager;
msgMan.loadFrameScript(FRAME_SCRIPT_URL, false);
msgMan.sendAsyncMessage("BrowserNewTabPreloader:allowBackgroundCaptures");
}
return false;
return swapped;
}
};

View File

@ -50,6 +50,7 @@ html|*.highlighter-nodeinfobar-pseudo-classes {
.highlighter-nodeinfobar-button {
-moz-appearance: none;
border-width: 0;
padding: 0;
width: 26px;
min-height: 26px;

View File

@ -68,14 +68,14 @@ function submitForm(idNum) {
function submitFormMouse(idNum) {
$("test"+idNum).setAttribute("onload", "frameLoaded(this)");
// Use 4.999 instead of 5 to guard against the possibility that the
// Use 4.99 instead of 5 to guard against the possibility that the
// image's 'top' is exactly N + 0.5 pixels from the root. In that case
// we'd round up the widget mouse coordinate to N + 6, which relative
// to the image would be 5.5, which would get rounded up to 6 when
// submitting the form. Instead we round the widget mouse coordinate to
// N + 5, which relative to the image would be 4.5 which gets rounded up
// to 5.
synthesizeMouse($("test" + idNum + "image"), 4.999, 4.999, {});
synthesizeMouse($("test" + idNum + "image"), 4.99, 4.99, {});
}
addLoadEvent(function() {

View File

@ -651,6 +651,9 @@ static const dom::ConstantSpec gWinProperties[] =
INT_CONSTANT(MOVEFILE_COPY_ALLOWED),
INT_CONSTANT(MOVEFILE_REPLACE_EXISTING),
// GetFileAttributes error constant
INT_CONSTANT(INVALID_FILE_ATTRIBUTES),
// Errors
INT_CONSTANT(ERROR_ACCESS_DENIED),
INT_CONSTANT(ERROR_DIR_NOT_EMPTY),

View File

@ -134,7 +134,8 @@ Object.defineProperty(OSError.prototype, "becauseExists", {
*/
Object.defineProperty(OSError.prototype, "becauseNoSuchFile", {
get: function becauseNoSuchFile() {
return this.winLastError == Const.ERROR_FILE_NOT_FOUND;
return this.winLastError == Const.ERROR_FILE_NOT_FOUND ||
this.winLastError == Const.ERROR_PATH_NOT_FOUND;
}
});
/**

View File

@ -345,6 +345,17 @@
"FlushFileBuffers", ctypes.winapi_abi,
/*return*/ Type.zero_or_nothing,
/*file*/ Type.HANDLE);
declareLazyFFI(SysFile, "GetFileAttributes", libc,
"GetFileAttributesW", ctypes.winapi_abi,
/*return*/ Type.DWORD,
/*fileName*/ Type.path);
declareLazyFFI(SysFile, "SetFileAttributes", libc,
"SetFileAttributesW", ctypes.winapi_abi,
/*return*/ Type.zero_or_nothing,
/*fileName*/ Type.path,
/*fileAttributes*/ Type.DWORD);
};
exports.OS.Win = {

View File

@ -382,14 +382,27 @@
* @throws {OS.File.Error} In case of I/O error.
*/
File.remove = function remove(path, options = {}) {
let result = WinFile.DeleteFile(path);
if (!result) {
if ((!("ignoreAbsent" in options) || options.ignoreAbsent) &&
ctypes.winLastError == Const.ERROR_FILE_NOT_FOUND) {
if (WinFile.DeleteFile(path)) {
return;
}
if (ctypes.winLastError == Const.ERROR_FILE_NOT_FOUND) {
if ((!("ignoreAbsent" in options) || options.ignoreAbsent)) {
return;
}
throw new File.Error("remove");
} else if (ctypes.winLastError == Const.ERROR_ACCESS_DENIED) {
let attributes = WinFile.GetFileAttributes(path);
if (attributes != Const.INVALID_FILE_ATTRIBUTES &&
attributes & Const.FILE_ATTRIBUTE_READONLY) {
let newAttributes = attributes & ~Const.FILE_ATTRIBUTE_READONLY;
if (WinFile.SetFileAttributes(path, newAttributes) &&
WinFile.DeleteFile(path)) {
return;
}
}
}
throw new File.Error("remove");
};
/**

View File

@ -151,7 +151,6 @@ let test = maketest("Main", function main(test) {
SimpleTest.waitForExplicitFinish();
yield test_constants();
yield test_path();
yield test_open();
yield test_stat();
yield test_debug();
yield test_info_features_detect();
@ -199,50 +198,6 @@ let test_path = maketest("path", function path(test) {
});
});
/**
* Test OS.File.open for reading:
* - with an existing file (should succeed);
* - with a non-existing file (should fail);
* - with inconsistent arguments (should fail).
*/
let test_open = maketest("open", function open(test) {
return Task.spawn(function() {
// Attempt to open a file that does not exist, ensure that it yields the
// appropriate error.
try {
let fd = yield OS.File.open(OS.Path.join(".", "This file does not exist"));
test.ok(false, "File opening 1 succeeded (it should fail)" + fd);
} catch (err) {
test.ok(true, "File opening 1 failed " + err);
test.ok(err instanceof OS.File.Error, "File opening 1 returned a file error");
test.ok(err.becauseNoSuchFile, "File opening 1 informed that the file does not exist");
}
// Attempt to open a file with the wrong args, so that it fails before
// serialization, ensure that it yields the appropriate error.
test.info("Attempting to open a file with wrong arguments");
try {
let fd = yield OS.File.open(1, 2, 3);
test.ok(false, "File opening 2 succeeded (it should fail)" + fd);
} catch (err) {
test.ok(true, "File opening 2 failed " + err);
test.ok(!(err instanceof OS.File.Error), "File opening 2 returned something that is not a file error");
test.ok(err.constructor.name == "TypeError", "File opening 2 returned a TypeError");
}
// Attempt to open a file correctly
test.info("Attempting to open a file correctly");
let openedFile = yield OS.File.open(EXISTING_FILE);
test.ok(true, "File opened correctly");
test.info("Attempting to close a file correctly");
yield openedFile.close();
test.info("Attempting to close a file again");
yield openedFile.close();
});
});
/**
* Test OS.File.stat and OS.File.prototype.stat
*/

View File

@ -844,4 +844,16 @@ function test_remove_file()
OS.File.remove(absent_file_name);
});
ok(!exn, "test_remove_file: ignoreAbsent works");
if (OS.Win) {
let file_name = "test_osfile_front_file_to_remove.tmp";
let file = OS.File.open(file_name, {write: true});
file.close();
ok(OS.File.exists(file_name), "test_remove_file: test file exists");
OS.Win.File.SetFileAttributes(file_name,
OS.Constants.Win.FILE_ATTRIBUTE_READONLY);
OS.File.remove(file_name);
ok(!OS.File.exists(file_name),
"test_remove_file: test file has been removed");
}
}

View File

@ -0,0 +1,75 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Components.utils.import("resource://gre/modules/osfile.jsm");
function run_test() {
do_test_pending();
run_next_test();
}
/**
* Test OS.File.open for reading:
* - with an existing file (should succeed);
* - with a non-existing file (should fail);
* - with inconsistent arguments (should fail).
*/
add_task(function() {
// Attempt to open a file that does not exist, ensure that it yields the
// appropriate error.
try {
let fd = yield OS.File.open(OS.Path.join(".", "This file does not exist"));
do_check_true(false, "File opening 1 succeeded (it should fail)");
} catch (err if err instanceof OS.File.Error && err.becauseNoSuchFile) {
do_print("File opening 1 failed " + err);
}
// Attempt to open a file with the wrong args, so that it fails before
// serialization, ensure that it yields the appropriate error.
do_print("Attempting to open a file with wrong arguments");
try {
let fd = yield OS.File.open(1, 2, 3);
do_check_true(false, "File opening 2 succeeded (it should fail)" + fd);
} catch (err) {
do_print("File opening 2 failed " + err);
do_check_false(err instanceof OS.File.Error,
"File opening 2 returned something that is not a file error");
do_check_true(err.constructor.name == "TypeError",
"File opening 2 returned a TypeError");
}
// Attempt to open a file correctly
do_print("Attempting to open a file correctly");
let openedFile = yield OS.File.open(OS.Path.join(do_get_cwd().path, "test_open.js"));
do_print("File opened correctly");
do_print("Attempting to close a file correctly");
yield openedFile.close();
do_print("Attempting to close a file again");
yield openedFile.close();
});
/**
* Test the error thrown by OS.File.open when attempting to open a directory
* that does not exist.
*/
add_task(function test_error_attributes () {
let dir = OS.Path.join(do_get_profile().path, "test_osfileErrorAttrs");
let fpath = OS.Path.join(dir, "test_error_attributes.txt");
try {
yield OS.File.open(fpath, {truncate: true}, {});
do_check_true(false, "Opening path suceeded (it should fail) " + fpath);
} catch (err) {
do_check_true(err instanceof OS.File.Error);
do_check_true(err.becauseNoSuchFile);
}
});
add_task(function() {
do_test_finished();
});

View File

@ -19,3 +19,4 @@ tail =
[test_reset.js]
[test_shutdown.js]
[test_unique.js]
[test_open.js]

View File

@ -1,9 +1,27 @@
/*
* Initialization: for each test, remove any prior notifications.
*/
function cleanUpPopupNotifications() {
var container = getPopupNotifications(window.top);
var notes = container._currentNotifications;
info(true, "Removing " + notes.length + " popup notifications.");
for (var i = notes.length-1; i >= 0; i--) {
notes[i].remove();
}
}
cleanUpPopupNotifications();
/*
* getPopupNotifications
*
* Fetches the popup notification for the specified window.
*/
function getPopupNotifications(aWindow) {
var Ci = SpecialPowers.Ci;
var Cc = SpecialPowers.Cc;
ok(Ci != null, "Access Ci");
ok(Cc != null, "Access Cc");
var chromeWin = SpecialPowers.wrap(aWindow)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)

View File

@ -1190,8 +1190,8 @@ Engine.prototype = {
_updateURL: null,
// The url to check for a new icon
_iconUpdateURL: null,
// A reference to the timer used for lazily serializing the engine to file
_serializeTimer: null,
/* Deferred serialization task. */
_lazySerializeTask: null,
/**
* Retrieves the data from the engine's file. If the engine's dataType is
@ -2244,28 +2244,15 @@ Engine.prototype = {
return doc;
},
_lazySerializeToFile: function SRCH_ENG_serializeToFile() {
if (this._serializeTimer) {
// Reset the timer
this._serializeTimer.delay = LAZY_SERIALIZE_DELAY;
} else {
this._serializeTimer = Cc["@mozilla.org/timer;1"].
createInstance(Ci.nsITimer);
var timerCallback = {
self: this,
notify: function SRCH_ENG_notify(aTimer) {
try {
this.self._serializeToFile();
} catch (ex) {
LOG("Serialization from timer callback failed:\n" + ex);
}
this.self._serializeTimer = null;
}
};
this._serializeTimer.initWithCallback(timerCallback,
LAZY_SERIALIZE_DELAY,
Ci.nsITimer.TYPE_ONE_SHOT);
get lazySerializeTask() {
if (!this._lazySerializeTask) {
let task = function taskCallback() {
this._serializeToFile();
}.bind(this);
this._lazySerializeTask = new DeferredTask(task, LAZY_SERIALIZE_DELAY);
}
return this._lazySerializeTask;
},
/**
@ -2296,6 +2283,9 @@ Engine.prototype = {
}
closeSafeOutputStream(fos);
Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC,
"write-engine-to-disk-complete");
},
/**
@ -2541,7 +2531,7 @@ Engine.prototype = {
url.addParam(aName, aValue);
// Serialize the changes to file lazily
this._lazySerializeToFile();
this.lazySerializeTask.start();
},
#ifdef ANDROID
@ -2895,25 +2885,17 @@ SearchService.prototype = {
return false;
},
_batchTimer: null,
_batchCacheInvalidation: function SRCH_SVC__batchCacheInvalidation() {
let callback = {
self: this,
notify: function SRCH_SVC_batchTimerNotify(aTimer) {
LOG("_batchCacheInvalidation: Invalidating engine cache");
this.self._buildCache();
this.self._batchTimer = null;
}
};
if (!this._batchTimer) {
this._batchTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._batchTimer.initWithCallback(callback, CACHE_INVALIDATION_DELAY,
Ci.nsITimer.TYPE_ONE_SHOT);
} else {
this._batchTimer.delay = CACHE_INVALIDATION_DELAY;
LOG("_batchCacheInvalidation: Batch timer reset");
_batchTask: null,
get batchTask() {
if (!this._batchTask) {
let task = function taskCallback() {
LOG("batchTask: Invalidating engine cache");
this._buildCache();
}.bind(this);
this._batchTask = new DeferredTask(task, CACHE_INVALIDATION_DELAY);
}
return this._batchTask;
},
_addEngineToStore: function SRCH_SVC_addEngineToStore(aEngine) {
@ -3403,7 +3385,7 @@ SearchService.prototype = {
engine._initFromMetadata(aName, aIconURL, aAlias, aDescription,
aMethod, aTemplate);
this._addEngineToStore(engine);
this._batchCacheInvalidation();
this.batchTask.start();
},
addEngine: function SRCH_SVC_addEngine(aEngineURL, aDataType, aIconURL,
@ -3466,10 +3448,10 @@ SearchService.prototype = {
engineToRemove.hidden = true;
engineToRemove.alias = null;
} else {
// Cancel the lazy serialization timer if it's running
if (engineToRemove._serializeTimer) {
engineToRemove._serializeTimer.cancel();
engineToRemove._serializeTimer = null;
// Cancel the serialized task if it's running
if (engineToRemove._lazySerializeTask) {
engineToRemove._lazySerializeTask.cancel();
engineToRemove._lazySerializeTask = null;
}
// Remove the engine file from disk (this might throw)
@ -3667,21 +3649,20 @@ SearchService.prototype = {
LOG("nsSearchService::observe: setting current");
this.currentEngine = aEngine;
}
this._batchCacheInvalidation();
this.batchTask.start();
break;
case SEARCH_ENGINE_CHANGED:
case SEARCH_ENGINE_REMOVED:
this._batchCacheInvalidation();
this.batchTask.start();
break;
}
break;
case QUIT_APPLICATION_TOPIC:
this._removeObservers();
if (this._batchTimer) {
if (this._batchTask) {
// Flush to disk immediately
this._batchTimer.cancel();
this._buildCache();
this._batchTask.flush();
}
engineMetadataService.flush();
break;

View File

@ -0,0 +1,88 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* test_serialize_file: Add test engines
*
* Ensure that :
* - File is created.
* - File size is bigger than 0.
* - lazySerializeTask updates the file.
*/
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/NetUtil.jsm");
add_test(function test_batchTask() {
let observer = function(aSubject, aTopic, aData) {
if (aTopic == "browser-search-engine-modified" && aData == "engine-loaded") {
let engine1 = Services.search.getEngineByName("Test search engine");
let engine2 = Services.search.getEngineByName("Sherlock test search engine");
if (engine1 && engine2) {
Services.obs.removeObserver(observer, aTopic);
// Test that files are written correctly.
let engineFile1 = engine1.wrappedJSObject._file;
let engineFile2 = engine2.wrappedJSObject._file;
do_check_true(engineFile1.exists());
do_check_true(engineFile2.exists());
do_check_neq(engineFile1.fileSize, 0);
do_check_neq(engineFile2.fileSize, 0);
run_next_test();
}
}
}
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
Services.search.addEngine("http://localhost:4444/data/engine.xml",
Ci.nsISearchEngine.DATA_XML, null, false);
Services.search.addEngine("http://localhost:4444/data/engine.src",
Ci.nsISearchEngine.DATA_TEXT,
"http://localhost:4444/data/ico-size-16x16-png.ico",
false);
});
add_test(function test_addParam() {
let engine = Services.search.getEngineByName("Test search engine");
engine.addParam("param-name", "param-value", null);
function readAsyncFile(aFile, aCallback) {
NetUtil.asyncFetch(aFile, function(inputStream, status) {
do_check_true(Components.isSuccessCode(status));
let data = NetUtil.readInputStreamToString(inputStream, inputStream.available());
aCallback(data);
});
}
let observer = function(aSubject, aTopic, aData) {
if (aTopic == "browser-search-service" &&
aData == "write-engine-to-disk-complete") {
Services.obs.removeObserver(observer, aTopic);
let engineFile = engine.wrappedJSObject._file;
readAsyncFile(engineFile, function(engineData) {
do_check_true(engineData.indexOf("param-name") > 0);
run_next_test();
});
}
}
Services.obs.addObserver(observer, "browser-search-service", false);
});
function run_test() {
updateAppInfo();
let httpServer = new HttpServer();
httpServer.start(4444);
httpServer.registerDirectory("/", do_get_cwd());
do_register_cleanup(function cleanup() {
httpServer.stop(function() {});
});
run_next_test();
}

View File

@ -28,3 +28,4 @@ support-files =
[test_prefSync.js]
[test_notifications.js]
[test_addEngine_callback.js]
[test_serialize_file.js]

View File

@ -27,6 +27,8 @@ loader:
5. Prepend `module.exports = ` to the package.json file contents, so that the
JSON data is exported, and we can load package.json as a module.
Bug 933482: Note, this is a workaround for Bug 910594, which will allow the SDK loader to require JSON files. To remove ambiguity, comment out the `require('./package.json').version` line in `escodegen.js` so that when Bug 910594 is uplifted into central, it does not attempt to look for `package.json`, rather than `package.json.js`. This is a temporary workaround, and once Bug 933500 is solved, either `package.json` or `package.json.js` will work.
6. Copy the estraverse.js that escodegen depends on into our tree:
$ cp node_modules/estraverse/estraverse.js /path/to/mozilla-central/devtools/escodegen/estraverse.js

View File

@ -2055,7 +2055,7 @@
FORMAT_DEFAULTS = getDefaultOptions().format;
exports.version = require('./package.json').version;
// exports.version = require('./package.json').version;
exports.generate = generate;
exports.attachComments = estraverse.attachComments;
exports.browser = false;

View File

@ -184,7 +184,9 @@ let WebGLActor = exports.WebGLActor = protocol.ActorClass({
protocol.Actor.prototype.initialize.call(this, conn);
this.tabActor = tabActor;
this._onGlobalCreated = this._onGlobalCreated.bind(this);
this._onGlobalDestroyed = this._onGlobalDestroyed.bind(this);
this._onProgramLinked = this._onProgramLinked.bind(this);
this._programActorsCache = [];
},
destroy: function(conn) {
protocol.Actor.prototype.destroy.call(this, conn);
@ -198,7 +200,7 @@ let WebGLActor = exports.WebGLActor = protocol.ActorClass({
*
* See ContentObserver and WebGLInstrumenter for more details.
*/
setup: method(function() {
setup: method(function({ reload }) {
if (this._initialized) {
return;
}
@ -206,10 +208,14 @@ let WebGLActor = exports.WebGLActor = protocol.ActorClass({
this._contentObserver = new ContentObserver(this.tabActor);
this._webglObserver = new WebGLObserver();
on(this._contentObserver, "global-created", this._onGlobalCreated);
on(this._contentObserver, "global-destroyed", this._onGlobalDestroyed);
on(this._webglObserver, "program-linked", this._onProgramLinked);
this.tabActor.window.location.reload();
if (reload) {
this.tabActor.window.location.reload();
}
}, {
request: { reload: Option(0, "boolean") },
oneway: true
}),
@ -225,11 +231,23 @@ let WebGLActor = exports.WebGLActor = protocol.ActorClass({
this._initialized = false;
this._contentObserver.stopListening();
off(this._contentObserver, "global-created", this._onGlobalCreated);
off(this._contentObserver, "global-destroyed", this._onGlobalDestroyed);
off(this._webglObserver, "program-linked", this._onProgramLinked);
}, {
oneway: true
}),
/**
* Gets an array of cached program actors for the current tab actor's window.
* This is useful for dealing with bfcache, when no new programs are linked.
*/
getPrograms: method(function() {
let id = getInnerWindowID(this.tabActor.window);
return this._programActorsCache.filter(e => e.owner == id).map(e => e.actor);
}, {
response: { programs: RetVal("array:gl-program") }
}),
/**
* Events emitted by this actor. The "program-linked" event is fired
* every time a WebGL program was linked with its respective two shaders.
@ -248,6 +266,14 @@ let WebGLActor = exports.WebGLActor = protocol.ActorClass({
WebGLInstrumenter.handle(window, this._webglObserver);
},
/**
* Invoked whenever the current tab actor's inner window is destroyed.
*/
_onGlobalDestroyed: function(id) {
this._programActorsCache =
this._programActorsCache.filter(e => e.owner != id);
},
/**
* Invoked whenever the current WebGL context links a program.
*/
@ -275,6 +301,11 @@ let WebGLActor = exports.WebGLActor = protocol.ActorClass({
programActor.program = program;
programActor.shadersData = shadersData;
this._programActorsCache.push({
owner: getInnerWindowID(this.tabActor.window),
actor: programActor
});
events.emit(this, "program-linked", programActor);
}
});
@ -297,8 +328,9 @@ let WebGLFront = exports.WebGLFront = protocol.FrontClass(WebGLActor, {
* instrument the HTMLCanvasElement with the appropriate inspection methods.
*/
function ContentObserver(tabActor) {
this._contentWindow = tabActor.browser.contentWindow;
this._contentWindow = tabActor.window;
this._onContentGlobalCreated = this._onContentGlobalCreated.bind(this);
this._onInnerWindowDestroyed = this._onInnerWindowDestroyed.bind(this);
this.startListening();
}
@ -309,6 +341,8 @@ ContentObserver.prototype = {
startListening: function() {
Services.obs.addObserver(
this._onContentGlobalCreated, "content-document-global-created", false);
Services.obs.addObserver(
this._onInnerWindowDestroyed, "inner-window-destroyed", false);
},
/**
@ -317,6 +351,8 @@ ContentObserver.prototype = {
stopListening: function() {
Services.obs.removeObserver(
this._onContentGlobalCreated, "content-document-global-created", false);
Services.obs.removeObserver(
this._onInnerWindowDestroyed, "inner-window-destroyed", false);
},
/**
@ -326,6 +362,14 @@ ContentObserver.prototype = {
if (subject == this._contentWindow) {
emit(this, "global-created", subject);
}
},
/**
* Fired when an inner window is removed from the backward/forward cache.
*/
_onInnerWindowDestroyed: function(subject, topic, data) {
let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
emit(this, "global-destroyed", id);
}
};
@ -849,3 +893,10 @@ WebGLProxy.prototype = {
return result;
}
};
function getInnerWindowID(window) {
return window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.currentInnerWindowID;
}

View File

@ -177,16 +177,64 @@ let PendingErrors = {
lineNumber: null
};
try { // Defend against non-enumerable values
if (typeof error == "object" && error) {
if (error && error instanceof Ci.nsIException) {
// nsIException does things a little differently.
try {
// For starters |.toString()| does not only contain the message, but
// also the top stack frame, and we don't really want that.
value.message = error.message;
} catch (ex) {
// Ignore field
}
try {
// All lowercase filename. ;)
value.fileName = error.filename;
} catch (ex) {
// Ignore field
}
try {
value.lineNumber = error.lineNumber;
} catch (ex) {
// Ignore field
}
} else if (typeof error == "object" && error) {
for (let k of ["fileName", "stack", "lineNumber"]) {
try { // Defend against fallible getters and string conversions
let v = error[k];
value[k] = v ? ("" + v):null;
value[k] = v ? ("" + v) : null;
} catch (ex) {
// Ignore field
}
}
}
if (!value.stack) {
// |error| is not an Error (or Error-alike). Try to figure out the stack.
let stack = null;
if (error && error.location &&
error.location instanceof Ci.nsIStackFrame) {
// nsIException has full stack frames in the |.location| member.
stack = error.location;
} else {
// Components.stack to the rescue!
stack = Components.stack;
// Remove those top frames that refer to Promise.jsm.
while (stack) {
if (!stack.filename.endsWith("/Promise.jsm")) {
break;
}
stack = stack.caller;
}
}
if (stack) {
let frames = [];
while (stack) {
frames.push(stack);
stack = stack.caller;
}
value.stack = frames.join("\n");
}
}
} catch (ex) {
// Ignore value
}

View File

@ -757,14 +757,20 @@ function wait_for_uncaught(aMustAppear, aTimeout = undefined) {
let make_string_rejection = function make_string_rejection() {
let salt = (Math.random() * ( Math.pow(2, 24) - 1 ));
let string = "This is an uncaught rejection " + salt;
return {mustFind: [string], error: string};
// Our error is not Error-like nor an nsIException, so the stack will
// include the closure doing the actual rejection.
return {mustFind: ["test_rejection_closure", string], error: string};
};
let make_num_rejection = function make_num_rejection() {
let salt = (Math.random() * ( Math.pow(2, 24) - 1 ));
return {mustFind: [salt], error: salt};
// Our error is not Error-like nor an nsIException, so the stack will
// include the closure doing the actual rejection.
return {mustFind: ["test_rejection_closure", salt], error: salt};
};
let make_undefined_rejection = function make_undefined_rejection() {
return {mustFind: [], error: undefined};
// Our error is not Error-like nor an nsIException, so the stack will
// include the closure doing the actual rejection.
return {mustFind: ["test_rejection_closure"], error: undefined};
};
let make_error_rejection = function make_error_rejection() {
let salt = (Math.random() * ( Math.pow(2, 24) - 1 ));
@ -774,16 +780,26 @@ function wait_for_uncaught(aMustAppear, aTimeout = undefined) {
error: error
};
};
let make_exception_rejection = function make_exception_rejection() {
let salt = (Math.random() * ( Math.pow(2, 24) - 1 ));
let exn = new Components.Exception("This is an uncaught exception " + salt,
Components.results.NS_ERROR_NOT_AVAILABLE);
return {
mustFind: [exn.message, exn.filename, exn.lineNumber, exn.location.toString()],
error: exn
};
};
for (let make_rejection of [make_string_rejection,
make_num_rejection,
make_undefined_rejection,
make_error_rejection]) {
make_error_rejection,
make_exception_rejection]) {
let {mustFind, error} = make_rejection();
let name = make_rejection.name;
tests.push(make_promise_test(function test_uncaught_is_reported() {
do_print("Testing with rejection " + name);
let promise = wait_for_uncaught(mustFind);
(function() {
(function test_rejection_closure() {
// For the moment, we cannot be absolutely certain that a value is
// garbage-collected, even if it is not referenced anymore, due to
// the conservative stack-scanning algorithm.