This commit is contained in:
Wes Kocher 2013-11-26 19:51:52 -06:00
Родитель 9d54e7a157 9c362e6779
Коммит 7815ce9f0b
78 изменённых файлов: 7052 добавлений и 6330 удалений

Просмотреть файл

@ -87,7 +87,8 @@ let Agent = {
return {
result: this.initialState,
telemetry: {FX_SESSION_RESTORE_READ_FILE_MS: durationMs}
telemetry: {FX_SESSION_RESTORE_READ_FILE_MS: durationMs,
FX_SESSION_RESTORE_FILE_SIZE_BYTES: bytes.byteLength}
};
} catch (ex if isNoSuchFileEx(ex)) {
// Ignore exceptions about non-existent files.

Просмотреть файл

@ -168,6 +168,11 @@ let TabStateInternal = {
// text and scroll data.
let history = yield Messenger.send(tab, "SessionStore:collectSessionHistory");
// The tab could have been closed while waiting for a response.
if (!tab.linkedBrowser) {
return;
}
// Collect basic tab data, without session history and storage.
let tabData = this._collectBaseTabData(tab);

Просмотреть файл

@ -29,13 +29,13 @@ addMessageListener("ss-test:getStyleSheets", function (msg) {
addMessageListener("ss-test:enableStyleSheetsForSet", function (msg) {
content.document.enableStyleSheetsForSet(msg.data);
sendSyncMessage("ss-test:enableStyleSheetsForSet");
sendAsyncMessage("ss-test:enableStyleSheetsForSet");
});
addMessageListener("ss-test:enableSubDocumentStyleSheetsForSet", function (msg) {
let iframe = content.document.getElementById(msg.data.id);
iframe.contentDocument.enableStyleSheetsForSet(msg.data.set);
sendSyncMessage("ss-test:enableSubDocumentStyleSheetsForSet");
sendAsyncMessage("ss-test:enableSubDocumentStyleSheetsForSet");
});
addMessageListener("ss-test:getAuthorStyleDisabled", function (msg) {

Просмотреть файл

@ -91,9 +91,6 @@ function testImageTooltip(index) {
assertTooltipShownOn(target, () => {
let images = markup.tooltip.panel.getElementsByTagName("image");
is(images.length, 1, "Tooltip for [" + TEST_NODES[index] + "] contains an image");
if (isImg) {
compareImageData(node, images[0].src);
}
markup.tooltip.hide();

Просмотреть файл

@ -557,6 +557,17 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
this.refreshZebra();
},
/**
* Removes all network requests and closes the sidebar if open.
*/
clear: function() {
NetMonitorView.Sidebar.toggle(false);
$("#details-pane-toggle").disabled = true;
this.empty();
this.refreshSummary();
},
/**
* Predicates used when filtering items.
*

Просмотреть файл

@ -210,6 +210,11 @@
class="plain requests-menu-footer-label"
flex="1"
crop="end"/>
<button id="requests-menu-clear-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.clear()"
label="&netmonitorUI.footer.clear;">
</button>
</hbox>
</vbox>

Просмотреть файл

@ -26,6 +26,7 @@ support-files =
[browser_net_accessibility-01.js]
[browser_net_accessibility-02.js]
[browser_net_autoscroll.js]
[browser_net_clear.js]
[browser_net_content-type.js]
[browser_net_copy_url.js]
[browser_net_cyrillic-01.js]

Просмотреть файл

@ -0,0 +1,76 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the clear button empties the request menu.
*/
function test() {
initNetMonitor(SIMPLE_URL).then(([aTab, aDebuggee, aMonitor]) => {
info("Starting test... ");
let { document, $, NetMonitorView } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView;
let detailsPane = $("#details-pane");
let detailsPaneToggleButton = $('#details-pane-toggle');
let clearButton = $('#requests-menu-clear-button');
RequestsMenu.lazyUpdate = false;
// Make sure we start in a sane state
assertNoRequestState(RequestsMenu, detailsPaneToggleButton);
// Load one request and assert it shows up in the lis
aMonitor.panelWin.once(aMonitor.panelWin.EVENTS.NETWORK_EVENT, () => {
assertSingleRequestState(RequestsMenu, detailsPaneToggleButton);
// Click clear and make sure the requests are gone
EventUtils.sendMouseEvent({ type: "click" }, clearButton);
assertNoRequestState(RequestsMenu, detailsPaneToggleButton);
// Load a second request and make sure they still show up
aMonitor.panelWin.once(aMonitor.panelWin.EVENTS.NETWORK_EVENT, () => {
assertSingleRequestState(RequestsMenu, detailsPaneToggleButton);
// Make sure we can now open the details pane
NetMonitorView.toggleDetailsPane({ visible: true, animated: false });
ok(!detailsPane.hasAttribute("pane-collapsed") &&
!detailsPaneToggleButton.hasAttribute("pane-collapsed"),
"The details pane should be visible after clicking the toggle button.");
// Click clear and make sure the details pane closes
EventUtils.sendMouseEvent({ type: "click" }, clearButton);
assertNoRequestState(RequestsMenu, detailsPaneToggleButton);
ok(detailsPane.hasAttribute("pane-collapsed") &&
detailsPaneToggleButton.hasAttribute("pane-collapsed"),
"The details pane should not be visible clicking 'clear'.");
teardown(aMonitor).then(finish);
});
aDebuggee.location.reload();
});
aDebuggee.location.reload();
});
/**
* Asserts the state of the network monitor when one request has loaded
*/
function assertSingleRequestState(RequestsMenu, detailsPaneToggleButton) {
is(RequestsMenu.itemCount, 1,
"The request menu should have one item at this point.");
is(detailsPaneToggleButton.hasAttribute("disabled"), false,
"The pane toggle button should be enabled after a request is made.");
}
/**
* Asserts the state of the network monitor when no requests have loaded
*/
function assertNoRequestState(RequestsMenu, detailsPaneToggleButton) {
is(RequestsMenu.itemCount, 0,
"The request menu should be empty at this point.");
is(detailsPaneToggleButton.hasAttribute("disabled"), true,
"The pane toggle button should be disabled when the request menu is cleared.");
}
}

Просмотреть файл

@ -35,9 +35,6 @@ function runTests()
};
let lastMethodCalled = null;
sp.__noSuchMethod__ = function(aMethodName) {
lastMethodCalled = aMethodName;
};
for (let id in methodsAndItems) {
lastMethodCalled = null;
@ -46,7 +43,9 @@ function runTests()
let oldMethod = sp[methodName];
ok(oldMethod, "found method " + methodName + " in Scratchpad object");
delete sp[methodName];
sp[methodName] = () => {
lastMethodCalled = methodName;
}
let menu = doc.getElementById(id);
ok(menu, "found menuitem #" + id);
@ -64,7 +63,5 @@ function runTests()
sp[methodName] = oldMethod;
}
delete sp.__noSuchMethod__;
finish();
}

Просмотреть файл

@ -32,7 +32,7 @@ const EVENTS = {
};
const STRINGS_URI = "chrome://browser/locale/devtools/shadereditor.properties"
const HIGHLIGHT_COLOR = [1, 0, 0, 1]; // rgba
const HIGHLIGHT_TINT = [1, 0, 0.25, 1]; // rgba
const TYPING_MAX_DELAY = 500; // ms
const SHADERS_AUTOGROW_ITEMS = 4;
const GUTTER_ERROR_PANEL_OFFSET_X = 7; // px
@ -297,7 +297,7 @@ let ShadersListView = Heritage.extend(WidgetMethods, {
_onProgramMouseEnter: function(e) {
let sourceItem = this.getItemForElement(e.target, { noSiblings: true });
if (sourceItem && !sourceItem.attachment.isBlackBoxed) {
sourceItem.attachment.programActor.highlight(HIGHLIGHT_COLOR);
sourceItem.attachment.programActor.highlight(HIGHLIGHT_TINT);
if (e instanceof Event) {
e.preventDefault();

Просмотреть файл

@ -1,5 +1,6 @@
[DEFAULT]
support-files =
doc_blended-geometry.html
doc_multiple-contexts.html
doc_overlapping-geometry.html
doc_shader-order.html
@ -14,9 +15,11 @@ support-files =
[browser_se_editors-lazy-init.js]
[browser_se_first-run.js]
[browser_se_navigation.js]
[browser_se_programs-blackbox.js]
[browser_se_programs-blackbox-01.js]
[browser_se_programs-blackbox-02.js]
[browser_se_programs-cache.js]
[browser_se_programs-highlight.js]
[browser_se_programs-highlight-01.js]
[browser_se_programs-highlight-02.js]
[browser_se_programs-list.js]
[browser_se_shaders-edit-01.js]
[browser_se_shaders-edit-02.js]

Просмотреть файл

@ -84,7 +84,7 @@ function ifWebGLSupported() {
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
ok(true, "Highlighting didn't work while blackboxed (1).");
ok(true, "Highlighting shouldn't work while blackboxed (1).");
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) });
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) });
@ -93,7 +93,7 @@ function ifWebGLSupported() {
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
ok(true, "Highlighting didn't work while blackboxed (2).");
ok(true, "Highlighting shouldn't work while blackboxed (2).");
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) });
@ -101,7 +101,7 @@ function ifWebGLSupported() {
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
ok(true, "Highlighting didn't work while blackboxed (3).");
ok(true, "Highlighting shouldn't work while blackboxed (3).");
getBlackBoxCheckbox(panel, 0).click();
getBlackBoxCheckbox(panel, 1).click();
@ -133,9 +133,9 @@ function ifWebGLSupported() {
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) });
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: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 64, 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: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2");
ok(true, "The second program was correctly highlighted.");
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) });

Просмотреть файл

@ -0,0 +1,67 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if blackboxing a program works properly in tandem with blended
* overlapping geometry.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(BLENDED_GEOMETRY_CANVAS_URL);
let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
reload(target);
let firstProgramActor = yield once(gFront, "program-linked");
let secondProgramActor = yield once(gFront, "program-linked");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
ok(true, "The canvas was correctly drawn.");
getBlackBoxCheckbox(panel, 0).click();
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 0, b: 0, a: 127 }, true);
ok(true, "The first program was correctly blackboxed.");
getBlackBoxCheckbox(panel, 0).click();
getBlackBoxCheckbox(panel, 1).click();
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 127, g: 127, b: 127, a: 255 }, true);
ok(true, "The second program was correctly blackboxed.");
getBlackBoxCheckbox(panel, 1).click();
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
ok(true, "The two programs were correctly unblackboxed.");
getBlackBoxCheckbox(panel, 0).click();
getBlackBoxCheckbox(panel, 1).click();
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 0, b: 0, a: 255 }, true);
ok(true, "The two programs were correctly blackboxed again.");
getBlackBoxCheckbox(panel, 0).click();
getBlackBoxCheckbox(panel, 1).click();
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
ok(true, "The two programs were correctly unblackboxed again.");
yield teardown(panel);
finish();
}
function getBlackBoxCheckbox(aPanel, aIndex) {
return aPanel.panelWin.document.querySelectorAll(
".side-menu-widget-item-checkbox")[aIndex];
}
function once(aTarget, aEvent) {
let deferred = promise.defer();
aTarget.once(aEvent, deferred.resolve);
return deferred.promise;
}

Просмотреть файл

@ -47,9 +47,9 @@ function ifWebGLSupported() {
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) });
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: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 64, 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: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2");
ok(true, "The second program was correctly highlighted.");
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) });

Просмотреть файл

@ -0,0 +1,53 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if highlighting a program works properly in tandem with blended
* overlapping geometry.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(BLENDED_GEOMETRY_CANVAS_URL);
let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
reload(target);
let firstProgramActor = yield once(gFront, "program-linked");
let secondProgramActor = yield once(gFront, "program-linked");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
ok(true, "The canvas was correctly drawn.");
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 0) });
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 0, b: 32, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 0, b: 32, a: 127 }, true);
ok(true, "The first program was correctly highlighted.");
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) });
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) });
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 255, g: 0, b: 64, a: 255 }, true);
ok(true, "The second program was correctly highlighted.");
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) });
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
ok(true, "The two programs were correctly unhighlighted.");
yield teardown(panel);
finish();
}
function getItemLabel(aPanel, aIndex) {
return aPanel.panelWin.document.querySelectorAll(
".side-menu-widget-item-label")[aIndex];
}
function once(aTarget, aEvent) {
let deferred = promise.defer();
aTarget.once(aEvent, deferred.resolve);
return deferred.promise;
}

Просмотреть файл

@ -19,9 +19,9 @@ function ifWebGLSupported() {
yield checkShaderSource("The shader sources are correct before highlighting.");
ok(true, "The corner pixel colors are 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);
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 0, b: 255, a: 255 }, true);
yield programActor.highlight([0, 1, 0, 1]);
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
yield checkShaderSource("The shader sources are preserved after highlighting.");
ok(true, "The corner pixel colors are correct after highlighting.");

Просмотреть файл

@ -70,9 +70,9 @@ function ifWebGLSupported() {
ok(fragSource.contains("vec3 vFragmentColor;"),
"The previous correct fragment shader source was preserved.");
yield programActor.highlight([0, 0, 1, 1]);
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 0, b: 255, a: 255 }, true);
yield programActor.highlight([0, 1, 0, 1]);
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
ok(true, "Highlighting worked after setting a defective fragment source.");
yield programActor.unhighlight();

Просмотреть файл

@ -27,15 +27,18 @@ function ifWebGLSupported() {
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 ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
ok(true, "The corner pixel colors are 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.highlight([0, 1, 0, 1]);
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
ok(true, "The corner pixel colors are 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 ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
ok(true, "The corner pixel colors are correct after unhighlighting.");
});
}
}

Просмотреть файл

@ -81,15 +81,18 @@ function ifWebGLSupported() {
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 ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
ok(true, "The corner pixel colors are 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.highlight([0, 1, 0, 1]);
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
ok(true, "The corner pixel colors are 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 ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
ok(true, "The corner pixel colors are correct after unhighlighting.");
});
}

Просмотреть файл

@ -81,15 +81,18 @@ function ifWebGLSupported() {
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 ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
ok(true, "The corner pixel colors are 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.highlight([0, 1, 0, 1]);
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
ok(true, "The corner pixel colors are 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 ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
ok(true, "The corner pixel colors are correct after unhighlighting.");
});
}

Просмотреть файл

@ -0,0 +1,136 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>WebGL editor test page</title>
<script id="shader-vs" type="x-shader/x-vertex">
precision lowp float;
attribute vec3 aVertexPosition;
uniform float uDepth;
void main(void) {
gl_Position = vec4(aVertexPosition, uDepth);
}
</script>
<script id="shader-fs-0" type="x-shader/x-fragment">
precision lowp float;
void main(void) {
gl_FragColor = vec4(0.5, 0.5, 0.5, 1.0);
}
</script>
<script id="shader-fs-1" type="x-shader/x-fragment">
precision lowp float;
void main(void) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>
</head>
<body>
<canvas id="canvas" width="128" height="128"></canvas>
<script type="text/javascript;version=1.8">
"use strict";
let canvas, gl;
let program = [];
let squareVerticesPositionBuffer;
let vertexPositionAttribute = [];
let depthUniform = [];
window.onload = function() {
canvas = document.querySelector("canvas");
gl = canvas.getContext("webgl");
gl.clearColor(0.0, 0.0, 0.0, 1.0);
initProgram(0);
initProgram(1);
initBuffers();
drawScene();
}
function initProgram(i) {
let vertexShader = getShader("shader-vs");
let fragmentShader = getShader("shader-fs-" + i);
program[i] = gl.createProgram();
gl.attachShader(program[i], vertexShader);
gl.attachShader(program[i], fragmentShader);
gl.linkProgram(program[i]);
vertexPositionAttribute[i] = gl.getAttribLocation(program[i], "aVertexPosition");
gl.enableVertexAttribArray(vertexPositionAttribute[i]);
depthUniform[i] = gl.getUniformLocation(program[i], "uDepth");
}
function getShader(id) {
let script = document.getElementById(id);
let source = script.textContent;
let shader;
if (script.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (script.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
}
gl.shaderSource(shader, source);
gl.compileShader(shader);
return shader;
}
function initBuffers() {
squareVerticesPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
1.0, -1.0, 0.0,
-1.0, -1.0, 0.0
]), gl.STATIC_DRAW);
}
function drawScene() {
gl.clear(gl.COLOR_BUFFER_BIT);
for (let i = 0; i < 2; i++) {
gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesPositionBuffer);
gl.vertexAttribPointer(vertexPositionAttribute[i], 3, gl.FLOAT, false, 0, 0);
gl.useProgram(program[i]);
gl.uniform1f(depthUniform[i], i + 1);
blend(i);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
window.requestAnimationFrame(drawScene);
}
function blend(i) {
if (i == 0) {
gl.disable(gl.BLEND);
}
else if (i == 1) {
gl.enable(gl.BLEND);
gl.blendColor(0.5, 0, 0, 0.25);
gl.blendEquationSeparate(
gl.FUNC_REVERSE_SUBTRACT, gl.FUNC_SUBTRACT);
gl.blendFuncSeparate(
gl.CONSTANT_COLOR, gl.ONE_MINUS_CONSTANT_COLOR,
gl.ONE_MINUS_CONSTANT_COLOR, gl.CONSTANT_COLOR);
}
}
</script>
</body>
</html>

Просмотреть файл

@ -28,6 +28,7 @@ const SIMPLE_CANVAS_URL = EXAMPLE_URL + "doc_simple-canvas.html";
const SHADER_ORDER_URL = EXAMPLE_URL + "doc_shader-order.html";
const MULTIPLE_CONTEXTS_URL = EXAMPLE_URL + "doc_multiple-contexts.html";
const OVERLAPPING_GEOMETRY_CANVAS_URL = EXAMPLE_URL + "doc_overlapping-geometry.html";
const BLENDED_GEOMETRY_CANVAS_URL = EXAMPLE_URL + "doc_blended-geometry.html";
// All tests are asynchronous.
waitForExplicitFinish();

Просмотреть файл

@ -100,7 +100,7 @@ StyleEditorPanel.prototype = {
return;
}
let stylesheet = this._debuggee.styleSheetFromHref(href);
this.UI.selectStyleSheet(href, line - 1, col - 1);
this.UI.selectStyleSheet(href, line - 1, col ? col - 1 : 0);
},
/**

Просмотреть файл

@ -39,3 +39,4 @@ support-files =
[browser_styleeditor_reload.js]
[browser_styleeditor_sv_keynav.js]
[browser_styleeditor_sv_resize.js]
[browser_styleeditor_selectstylesheet.js]

Просмотреть файл

@ -0,0 +1,54 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html";
const NEW_URI = TEST_BASE_HTTPS + "media.html";
const LINE_NO = 5;
const COL_NO = 0;
let gContentWin;
let gUI;
function test()
{
waitForExplicitFinish();
addTabAndOpenStyleEditor(function(panel) {
gContentWin = gBrowser.selectedTab.linkedBrowser.contentWindow.wrappedJSObject;
gUI = panel.UI;
let count = 0;
gUI.on("editor-added", function editorAdded(event, editor) {
if (++count == 2) {
gUI.off("editor-added", editorAdded);
gUI.editors[0].getSourceEditor().then(runTests);
}
})
});
content.location = TESTCASE_URI;
}
function runTests()
{
let count = 0;
// Make sure Editor doesn't go into an infinite loop when
// column isn't passed. See bug 941018.
gUI.once("editor-selected", (event, editor) => {
editor.getSourceEditor().then(() => {
is(gUI.selectedEditor, gUI.editors[1], "second editor is selected");
let {line, ch} = gUI.selectedEditor.sourceEditor.getCursor();
is(line, LINE_NO, "correct line selected");
is(ch, COL_NO, "correct column selected");
gUI = null;
finish();
});
});
gUI.selectStyleSheet(gUI.editors[1].styleSheet.href, LINE_NO);
}

Просмотреть файл

@ -28,6 +28,8 @@ const BROWSER_CONSOLE_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,re
// The preference prefix for all of the Browser Console filters.
const BROWSER_CONSOLE_FILTER_PREFS_PREFIX = "devtools.browserconsole.filter.";
let gHudId = 0;
///////////////////////////////////////////////////////////////////////////
//// The HUD service
@ -301,7 +303,7 @@ function WebConsole(aTarget, aIframeWindow, aChromeWindow)
{
this.iframeWindow = aIframeWindow;
this.chromeWindow = aChromeWindow;
this.hudId = "hud_" + Date.now();
this.hudId = "hud_" + ++gHudId;
this.target = aTarget;
this.browserWindow = this.chromeWindow.top;

Просмотреть файл

@ -93,6 +93,7 @@ support-files =
test_bug_770099_bad_policy_uri.html^headers^
test_bug_770099_violation.html
test_bug_770099_violation.html^headers^
test-autocomplete-in-stackframe.html
testscript.js
test-bug_923281_console_log_filter.html
test-bug_923281_test1.js
@ -241,3 +242,4 @@ skip-if = os == "linux"
[browser_webconsole_reflow.js]
[browser_webconsole_log_file_filter.js]
[browser_webconsole_expandable_timestamps.js]
[browser_webconsole_autocomplete_in_debugger_stackframe.js]

Просмотреть файл

@ -0,0 +1,212 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Test that makes sure web console autocomplete happens in the user-selected stackframe
// from the js debugger.
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-autocomplete-in-stackframe.html";
let testDriver, gStackframes;
function test()
{
requestLongerTimeout(2);
addTab(TEST_URI);
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole(null, function(hud) {
testDriver = testCompletion(hud);
testDriver.next();
});
}, true);
}
function testNext() {
executeSoon(function() {
testDriver.next();
});
}
function testCompletion(hud) {
let jsterm = hud.jsterm;
let input = jsterm.inputNode;
let popup = jsterm.autocompletePopup;
// Test if 'f' gives 'foo1' but not 'foo2' or 'foo3'
input.value = "f";
input.setSelectionRange(1, 1);
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
yield undefined;
let newItems = popup.getItems();
ok(newItems.length > 0, "'f' gave a list of suggestions");
ok(!newItems.every(function(item) {
return item.label != "foo1";
}), "autocomplete results do contain foo1");
ok(!newItems.every(function(item) {
return item.label != "foo1Obj";
}), "autocomplete results do contain foo1Obj");
ok(newItems.every(function(item) {
return item.label != "foo2";
}), "autocomplete results do not contain foo2");
ok(newItems.every(function(item) {
return item.label != "foo2Obj";
}), "autocomplete results do not contain foo2Obj");
ok(newItems.every(function(item) {
return item.label != "foo3";
}), "autocomplete results do not contain foo3");
ok(newItems.every(function(item) {
return item.label != "foo3Obj";
}), "autocomplete results do not contain foo3Obj");
// Test if 'foo1Obj.' gives 'prop1' and 'prop2'
input.value = "foo1Obj.";
input.setSelectionRange(8, 8);
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
yield undefined;
newItems = popup.getItems();
ok(!newItems.every(function(item) {
return item.label != "prop1";
}), "autocomplete results do contain prop1");
ok(!newItems.every(function(item) {
return item.label != "prop2";
}), "autocomplete results do contain prop2");
// Test if 'foo1Obj.prop2.' gives 'prop21'
input.value = "foo1Obj.prop2.";
input.setSelectionRange(14, 14);
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
yield undefined;
newItems = popup.getItems();
ok(!newItems.every(function(item) {
return item.label != "prop21";
}), "autocomplete results do contain prop21");
info("openDebugger");
executeSoon(() => openDebugger().then(debuggerOpened));
yield undefined;
// From this point on the
// Test if 'f' gives 'foo3' and 'foo1' but not 'foo2'
input.value = "f";
input.setSelectionRange(1, 1);
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
yield undefined;
newItems = popup.getItems();
ok(newItems.length > 0, "'f' gave a list of suggestions");
ok(!newItems.every(function(item) {
return item.label != "foo3";
}), "autocomplete results do contain foo3");
ok(!newItems.every(function(item) {
return item.label != "foo3Obj";
}), "autocomplete results do contain foo3Obj");
ok(!newItems.every(function(item) {
return item.label != "foo1";
}), "autocomplete results do contain foo1");
ok(!newItems.every(function(item) {
return item.label != "foo1Obj";
}), "autocomplete results do contain foo1Obj");
ok(newItems.every(function(item) {
return item.label != "foo2";
}), "autocomplete results do not contain foo2");
ok(newItems.every(function(item) {
return item.label != "foo2Obj";
}), "autocomplete results do not contain foo2Obj");
openDebugger().then(() => {
gStackframes.selectFrame(1);
info("openConsole");
executeSoon(() => openConsole(null, () => testDriver.next()));
});
yield undefined;
// Test if 'f' gives 'foo2' and 'foo1' but not 'foo3'
input.value = "f";
input.setSelectionRange(1, 1);
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
yield undefined;
newItems = popup.getItems();
ok(newItems.length > 0, "'f' gave a list of suggestions");
ok(!newItems.every(function(item) {
return item.label != "foo2";
}), "autocomplete results do contain foo2");
ok(!newItems.every(function(item) {
return item.label != "foo2Obj";
}), "autocomplete results do contain foo2Obj");
ok(!newItems.every(function(item) {
return item.label != "foo1";
}), "autocomplete results do contain foo1");
ok(!newItems.every(function(item) {
return item.label != "foo1Obj";
}), "autocomplete results do contain foo1Obj");
ok(newItems.every(function(item) {
return item.label != "foo3";
}), "autocomplete results do not contain foo3");
ok(newItems.every(function(item) {
return item.label != "foo3Obj";
}), "autocomplete results do not contain foo3Obj");
// Test if 'foo2Obj.' gives 'prop1'
input.value = "foo2Obj.";
input.setSelectionRange(8, 8);
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
yield undefined;
newItems = popup.getItems();
ok(!newItems.every(function(item) {
return item.label != "prop1";
}), "autocomplete results do contain prop1");
// Test if 'foo1Obj.prop1.' gives 'prop11'
input.value = "foo2Obj.prop1.";
input.setSelectionRange(14, 14);
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
yield undefined;
newItems = popup.getItems();
ok(!newItems.every(function(item) {
return item.label != "prop11";
}), "autocomplete results do contain prop11");
// Test if 'foo1Obj.prop1.prop11.' gives suggestions for a string i.e. 'length'
input.value = "foo2Obj.prop1.prop11.";
input.setSelectionRange(21, 21);
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
yield undefined;
newItems = popup.getItems();
ok(!newItems.every(function(item) {
return item.label != "length";
}), "autocomplete results do contain length");
testDriver = null;
executeSoon(finishTest);
yield undefined;
}
function debuggerOpened(aResult)
{
let debuggerWin = aResult.panelWin;
let debuggerController = debuggerWin.DebuggerController;
let thread = debuggerController.activeThread;
gStackframes = debuggerController.StackFrames;
executeSoon(() => {
thread.addOneTimeListener("framesadded", onFramesAdded);
info("firstCall()");
content.wrappedJSObject.firstCall();
});
}
function onFramesAdded()
{
info("onFramesAdded, openConsole() now");
executeSoon(() => openConsole(null, testNext));
}

Просмотреть файл

@ -18,14 +18,14 @@ function consoleOpened(aHud) {
HUD = aHud;
info("web console opened");
content.wrappedJSObject.foobarBug585991 = {
"item0": "value0",
"item1": "value1",
"item2": "value2",
"item3": "value3",
};
jsterm = HUD.jsterm;
jsterm.execute("window.foobarBug585991={" +
"'item0': 'value0'," +
"'item1': 'value1'," +
"'item2': 'value2'," +
"'item3': 'value3'" +
"}");
popup = jsterm.autocompletePopup;
completeNode = jsterm.completeNode;
inputNode = jsterm.inputNode;

Просмотреть файл

@ -19,20 +19,25 @@ function consoleOpened(HUD) {
let tools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
let JSPropertyProvider = tools.require("devtools/toolkit/webconsole/utils").JSPropertyProvider;
let tmp = Cu.import("resource://gre/modules/jsdebugger.jsm", {});
tmp.addDebuggerToGlobal(tmp);
let dbg = new tmp.Debugger;
let jsterm = HUD.jsterm;
let win = content.wrappedJSObject;
let dbgWindow = dbg.makeGlobalObjectReference(win);
// Make sure autocomplete does not walk through iterators and generators.
let result = win.gen1.next();
let completion = JSPropertyProvider(win, "gen1.");
is(completion, null, "no matches for gen1");
let completion = JSPropertyProvider(dbgWindow, null, "gen1.");
isnot(completion.matches.length, 0, "Got matches for gen1");
is(result+1, win.gen1.next(), "gen1.next() did not execute");
result = win.gen2.next();
completion = JSPropertyProvider(win, "gen2.");
is(completion, null, "no matches for gen2");
completion = JSPropertyProvider(dbgWindow, null, "gen2.");
isnot(completion.matches.length, 0, "Got matches for gen2");
is((result/2+1)*2, win.gen2.next(),
"gen2.next() did not execute");
@ -41,17 +46,18 @@ function consoleOpened(HUD) {
is(result[0], "foo", "iter1.next() [0] is correct");
is(result[1], "bar", "iter1.next() [1] is correct");
completion = JSPropertyProvider(win, "iter1.");
is(completion, null, "no matches for iter1");
completion = JSPropertyProvider(dbgWindow, null, "iter1.");
isnot(completion.matches.length, 0, "Got matches for iter1");
result = win.iter1.next();
is(result[0], "baz", "iter1.next() [0] is correct");
is(result[1], "baaz", "iter1.next() [1] is correct");
completion = JSPropertyProvider(content, "iter2.");
is(completion, null, "no matches for iter2");
let dbgContent = dbg.makeGlobalObjectReference(content);
completion = JSPropertyProvider(dbgContent, null, "iter2.");
isnot(completion.matches.length, 0, "Got matches for iter2");
completion = JSPropertyProvider(win, "window.");
completion = JSPropertyProvider(dbgWindow, null, "window.");
ok(completion, "matches available for window");
ok(completion.matches.length, "matches available for window (length)");

Просмотреть файл

@ -18,17 +18,22 @@ function testPropertyProvider() {
let tools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
let JSPropertyProvider = tools.require("devtools/toolkit/webconsole/utils").JSPropertyProvider;
let completion = JSPropertyProvider(content, "thisIsNotDefined");
let tmp = Cu.import("resource://gre/modules/jsdebugger.jsm", {});
tmp.addDebuggerToGlobal(tmp);
let dbg = new tmp.Debugger;
let dbgWindow = dbg.makeGlobalObjectReference(content);
let completion = JSPropertyProvider(dbgWindow, null, "thisIsNotDefined");
is (completion.matches.length, 0, "no match for 'thisIsNotDefined");
// This is a case the PropertyProvider can't handle. Should return null.
completion = JSPropertyProvider(content, "window[1].acb");
completion = JSPropertyProvider(dbgWindow, null, "window[1].acb");
is (completion, null, "no match for 'window[1].acb");
// A very advanced completion case.
var strComplete =
'function a() { }document;document.getElementById(window.locatio';
completion = JSPropertyProvider(content, strComplete);
completion = JSPropertyProvider(dbgWindow, null, strComplete);
ok(completion.matches.length == 2, "two matches found");
ok(completion.matchProp == "locatio", "matching part is 'test'");
var matches = completion.matches;

Просмотреть файл

@ -0,0 +1,50 @@
<!DOCTYPE HTML>
<html dir="ltr" lang="en">
<head>
<meta charset="utf8">
<!--
- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/
-->
<title>Test for bug 842682 - use the debugger API for web console autocomplete</title>
<script>
var foo1 = "globalFoo";
var foo1Obj = {
prop1: "111",
prop2: {
prop21: "212121"
}
};
function firstCall()
{
var foo2 = "fooFirstCall";
var foo2Obj = {
prop1: {
prop11: "111111"
}
};
secondCall();
}
function secondCall()
{
var foo3 = "fooSecondCall";
var foo3Obj = {
prop1: {
prop11: "313131"
}
};
debugger;
}
</script>
</head>
<body>
<p>Hello world!</p>
</body>
</html>

Просмотреть файл

@ -3074,6 +3074,14 @@ JSTerm.prototype = {
*/
_autocompleteQuery: null,
/**
* The frameActorId used in the last autocomplete query. Whenever this changes
* the autocomplete cache must be invalidated.
* @private
* @type string
*/
_lastFrameActorId: null,
/**
* The Web Console sidebar.
* @see this._createSidebar()
@ -4286,6 +4294,8 @@ JSTerm.prototype = {
{
let inputNode = this.inputNode;
let inputValue = inputNode.value;
let frameActor = this.getFrameActor(this.SELECTED_FRAME);
// If the inputNode has no value, then don't try to complete on it.
if (!inputValue) {
this.clearCompletion();
@ -4299,7 +4309,7 @@ JSTerm.prototype = {
}
// Update the completion results.
if (this.lastCompletion.value != inputValue) {
if (this.lastCompletion.value != inputValue || frameActor != this._lastFrameActorId) {
this._updateCompletionResult(aType, aCallback);
return false;
}
@ -4335,7 +4345,8 @@ JSTerm.prototype = {
_updateCompletionResult:
function JST__updateCompletionResult(aType, aCallback)
{
if (this.lastCompletion.value == this.inputNode.value) {
let frameActor = this.getFrameActor(this.SELECTED_FRAME);
if (this.lastCompletion.value == this.inputNode.value && frameActor == this._lastFrameActorId) {
return;
}
@ -4350,7 +4361,7 @@ JSTerm.prototype = {
// character we ask the server again for suggestions.
// Check if last character is non-alphanumeric
if (!/[a-zA-Z0-9]$/.test(input)) {
if (!/[a-zA-Z0-9]$/.test(input) || frameActor != this._lastFrameActorId) {
this._autocompleteQuery = null;
this._autocompleteCache = null;
}
@ -4380,6 +4391,8 @@ JSTerm.prototype = {
return;
}
this._lastFrameActorId = frameActor;
this.lastCompletion = {
requestId: requestId,
completionType: aType,
@ -4388,7 +4401,8 @@ JSTerm.prototype = {
let callback = this._receiveAutocompleteProperties.bind(this, requestId,
aCallback);
this.webConsoleClient.autocomplete(input, cursor, callback);
this.webConsoleClient.autocomplete(input, cursor, callback, frameActor);
},
/**

Просмотреть файл

@ -72,7 +72,8 @@ function log(aMsg) {
}
function getDOMWindow(aChannel) {
var requestor = aChannel.notificationCallbacks;
var requestor = aChannel.notificationCallbacks ||
aChannel.loadGroup.notificationCallbacks;
var win = requestor.getInterface(Components.interfaces.nsIDOMWindow);
return win;
}
@ -177,7 +178,10 @@ function isShumwayEnabledFor(actions) {
// blacklisting well known sites with issues
if (/\.ytimg\.com\//i.test(url) /* youtube movies */ ||
/\/vui.swf\b/i.test(url) /* vidyo manager */ ) {
/\/vui.swf\b/i.test(url) /* vidyo manager */ ||
/soundcloud\.com\/player\/assets\/swf/i.test(url) /* soundcloud */ ||
/sndcdn\.com\/assets\/swf/.test(url) /* soundcloud */ ||
/vimeocdn\.com/.test(url) /* vimeo */) {
return false;
}
@ -727,34 +731,45 @@ ShumwayStreamConverterBase.prototype = {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
isValidRequest: function() {
return true;
},
getUrlHint: function(requestUrl) {
return requestUrl.spec;
},
createChromeActions: function(window, document, urlHint) {
var url;
var url = urlHint;
var baseUrl;
var pageUrl;
var element = window.frameElement;
var isOverlay = false;
var objectParams = {};
if (element) {
var tagName = element.nodeName;
// PlayPreview overlay "belongs" to the embed/object tag and consists of
// DIV and IFRAME. Starting from IFRAME and looking for first object tag.
var tagName = element.nodeName, containerElement;
while (tagName != 'EMBED' && tagName != 'OBJECT') {
// plugin overlay skipping until the target plugin is found
isOverlay = true;
containerElement = element;
element = element.parentNode;
if (!element)
throw 'Plugin element is not found';
if (!element) {
throw new Error('Plugin element is not found');
}
tagName = element.nodeName;
}
// TODO: remove hack once bug 920927 is fixed
element.style.visibility = 'visible';
if (isOverlay) {
// Checking if overlay is a proper PlayPreview overlay.
for (var i = 0; i < element.children.length; i++) {
if (element.children[i] === containerElement) {
throw new Error('Plugin element is invalid');
}
}
}
}
if (element) {
// Getting absolute URL from the EMBED tag
url = element.srcURI.spec;
pageUrl = element.ownerDocument.location.href; // proper page url?
@ -764,7 +779,6 @@ ShumwayStreamConverterBase.prototype = {
objectParams[paramName] = element.attributes[i].value;
}
} else {
url = element.getAttribute('data');
for (var i = 0; i < element.childNodes.length; ++i) {
var paramElement = element.childNodes[i];
if (paramElement.nodeType != 1 ||
@ -777,7 +791,10 @@ ShumwayStreamConverterBase.prototype = {
}
}
url = url || objectParams.src || objectParams.movie;
if (!url) { // at this point url shall be known -- asserting
throw new Error('Movie url is not specified');
}
baseUrl = objectParams.base || pageUrl;
var movieParams = {};
@ -794,9 +811,6 @@ ShumwayStreamConverterBase.prototype = {
}
}
url = !url ? urlHint : Services.io.newURI(url, null,
baseUrl ? Services.io.newURI(baseUrl, null, null) : null).spec;
var allowScriptAccess = false;
switch (objectParams.allowscriptaccess || 'sameDomain') {
case 'always':
@ -830,9 +844,6 @@ ShumwayStreamConverterBase.prototype = {
// nsIStreamConverter::asyncConvertData
asyncConvertData: function(aFromType, aToType, aListener, aCtxt) {
if(!this.isValidRequest(aCtxt))
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
// Store the listener passed to us
this.listener = aListener;
},
@ -847,8 +858,15 @@ ShumwayStreamConverterBase.prototype = {
onStartRequest: function(aRequest, aContext) {
// Setup the request so we can use it below.
aRequest.QueryInterface(Ci.nsIChannel);
// Cancel the request so the viewer can handle it.
aRequest.cancel(Cr.NS_BINDING_ABORTED);
aRequest.QueryInterface(Ci.nsIWritablePropertyBag);
// Change the content type so we don't get stuck in a loop.
aRequest.setProperty('contentType', aRequest.contentType);
aRequest.contentType = 'text/html';
// TODO For now suspending request, however we can continue fetching data
aRequest.suspend();
var originalURI = aRequest.URI;
@ -857,61 +875,74 @@ ShumwayStreamConverterBase.prototype = {
getBoolPref('shumway.simpleMode', false);
// Create a new channel that loads the viewer as a resource.
var channel = Services.io.newChannel(isSimpleMode ?
var viewerUrl = isSimpleMode ?
'resource://shumway/web/simple.html' :
'resource://shumway/web/viewer.html', null, null);
'resource://shumway/web/viewer.html';
var channel = Services.io.newChannel(viewerUrl, null, null);
var converter = this;
var listener = this.listener;
// Proxy all the request observer calls, when it gets to onStopRequest
// we can get the dom window.
var proxy = {
onStartRequest: function() {
listener.onStartRequest.apply(listener, arguments);
onStartRequest: function(request, context) {
listener.onStartRequest(aRequest, context);
},
onDataAvailable: function() {
listener.onDataAvailable.apply(listener, arguments);
onDataAvailable: function(request, context, inputStream, offset, count) {
listener.onDataAvailable(aRequest, context, inputStream, offset, count);
},
onStopRequest: function() {
onStopRequest: function(request, context, statusCode) {
// Cancel the request so the viewer can handle it.
aRequest.resume();
aRequest.cancel(Cr.NS_BINDING_ABORTED);
var domWindow = getDOMWindow(channel);
if (domWindow.document.documentURIObject.equals(channel.originalURI)) {
// Double check the url is still the correct one.
let actions = converter.createChromeActions(domWindow,
domWindow.document,
converter.getUrlHint(originalURI));
if (!isShumwayEnabledFor(actions)) {
actions.fallback(true);
return;
}
let actions = converter.createChromeActions(domWindow,
domWindow.document,
converter.getUrlHint(originalURI));
// Report telemetry on amount of swfs on the page
if (actions.isOverlay) {
// Looking for last actions with same baseUrl
var prevPageActions = ActivationQueue.findLastOnPage(actions.baseUrl);
var pageIndex = !prevPageActions ? 1 : (prevPageActions.telemetry.pageIndex + 1);
actions.telemetry.pageIndex = pageIndex;
ShumwayTelemetry.onPageIndex(pageIndex);
} else {
ShumwayTelemetry.onPageIndex(0);
}
actions.activationCallback = function(domWindow, isSimpleMode) {
delete this.activationCallback;
activateShumwayScripts(domWindow, isSimpleMode);
}.bind(actions, domWindow, isSimpleMode);
ActivationQueue.enqueue(actions);
let requestListener = new RequestListener(actions);
domWindow.addEventListener('shumway.message', function(event) {
requestListener.receive(event);
}, false, true);
if (!isShumwayEnabledFor(actions)) {
actions.fallback(true);
return;
}
listener.onStopRequest.apply(listener, arguments);
// Report telemetry on amount of swfs on the page
if (actions.isOverlay) {
// Looking for last actions with same baseUrl
var prevPageActions = ActivationQueue.findLastOnPage(actions.baseUrl);
var pageIndex = !prevPageActions ? 1 : (prevPageActions.telemetry.pageIndex + 1);
actions.telemetry.pageIndex = pageIndex;
ShumwayTelemetry.onPageIndex(pageIndex);
} else {
ShumwayTelemetry.onPageIndex(0);
}
actions.activationCallback = function(domWindow, isSimpleMode) {
delete this.activationCallback;
activateShumwayScripts(domWindow, isSimpleMode);
}.bind(actions, domWindow, isSimpleMode);
ActivationQueue.enqueue(actions);
let requestListener = new RequestListener(actions);
domWindow.addEventListener('shumway.message', function(event) {
requestListener.receive(event);
}, false, true);
listener.onStopRequest(aRequest, context, statusCode);
}
};
// XXX? Keep the URL the same so the browser sees it as the same.
// channel.originalURI = aRequest.URI;
// Keep the URL the same so the browser sees it as the same.
channel.originalURI = aRequest.URI;
channel.loadGroup = aRequest.loadGroup;
// We can use resource principal when data is fetched by the chrome
// e.g. useful for NoScript
var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1']
.getService(Ci.nsIScriptSecurityManager);
var uri = Services.io.newURI(viewerUrl, null, null);
var resourcePrincipal = securityManager.getNoAppCodebasePrincipal(uri);
aRequest.owner = resourcePrincipal;
channel.asyncOpen(proxy, aContext);
},
@ -943,17 +974,6 @@ copyProperties(ShumwayStreamOverlayConverter.prototype, {
classDescription: 'Shumway PlayPreview Component',
contractID: '@mozilla.org/streamconv;1?from=application/x-moz-playpreview&to=*/*'
});
ShumwayStreamOverlayConverter.prototype.isValidRequest =
(function(aCtxt) {
try {
var request = aCtxt;
request.QueryInterface(Ci.nsIChannel);
var spec = request.URI.spec;
return spec.indexOf(EXPECTED_PLAYPREVIEW_URI_PREFIX) === 0;
} catch (e) {
return false;
}
});
ShumwayStreamOverlayConverter.prototype.getUrlHint = function (requestUrl) {
return '';
};

Двоичный файл не отображается.

Двоичный файл не отображается.

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -1 +1 @@
0.7.502
0.7.806

Просмотреть файл

@ -120,6 +120,8 @@ function runViewer() {
}
var showURLMenu = document.getElementById('showURLMenu');
showURLMenu.addEventListener('click', showURL);
var inspectorMenu = document.getElementById('inspectorMenu');
inspectorMenu.addEventListener('click', showInInspector);
document.getElementById('copyProfileMenu').addEventListener('click', copyProfile);
}
@ -129,6 +131,15 @@ function showURL() {
window.prompt("Copy to clipboard", flashParams.url);
}
function showInInspector() {
var base = "http://www.areweflashyet.com/shumway/examples/inspector/inspector.html?rfile=";
var params = '';
for (var k in movieParams) {
params += '&' + k + '=' + encodeURIComponent(movieParams[k]);
}
window.open(base + encodeURIComponent(movieUrl) + params);
}
function copyProfile() {
function toArray(v) {
var array = [];

Просмотреть файл

@ -23,7 +23,7 @@ limitations under the License.
<script src="../lib/DataView.js/DataView.js"></script>
<!-- Load SWF Dependencies -->
<script src="../swf/util.js"></script>
<script src="../flash/util.js"></script>
<script src="../swf/inflate.js"></script>
<script src="../swf/stream.js"></script>

Просмотреть файл

@ -83,6 +83,7 @@ limitations under the License.
<menu type="context" id="shumwayMenu">
<menuitem label="Show URL" id="showURLMenu"></menuitem>
<menuitem label="Copy Profile" id="copyProfileMenu"></menuitem>
<menuitem label="Open in Inspector" id="inspectorMenu"></menuitem>
<menuitem label="Fallback to Flash" id="fallbackMenu" hidden></menuitem>
</menu>
</section>

Просмотреть файл

@ -99,6 +99,10 @@
- in the network details footer for the "Flash" filtering button. -->
<!ENTITY netmonitorUI.footer.filterFlash "Flash">
<!-- LOCALIZATION NOTE (debuggerUI.footer.clear): This is the label displayed
- in the network details footer for the "Clear" button. -->
<!ENTITY netmonitorUI.footer.clear "Clear">
<!-- LOCALIZATION NOTE (debuggerUI.panesButton.tooltip): This is the tooltip for
- the button that toggles the panes visible or hidden in the netmonitor UI. -->
<!ENTITY netmonitorUI.panesButton.tooltip "Toggle network info">

Просмотреть файл

@ -28,8 +28,7 @@ var APZCObserver = {
}
let os = Services.obs;
os.addObserver(this, "apzc-handle-pan-begin", false);
os.addObserver(this, "apzc-handle-pan-end", false);
os.addObserver(this, "apzc-transform-begin", false);
// Fired by ContentAreaObserver
window.addEventListener("SizeChanged", this, true);
@ -45,8 +44,7 @@ var APZCObserver = {
}
let os = Services.obs;
os.removeObserver(this, "apzc-handle-pan-begin");
os.removeObserver(this, "apzc-handle-pan-end");
os.removeObserver(this, "apzc-transform-begin");
window.removeEventListener("SizeChanged", this, true);
@ -86,7 +84,7 @@ var APZCObserver = {
},
observe: function ao_observe(aSubject, aTopic, aData) {
if (aTopic == "apzc-handle-pan-begin") {
if (aTopic == "apzc-transform-begin") {
// When we're panning, hide the main scrollbars by setting imprecise
// input (which sets a property on the browser which hides the scrollbar
// via CSS). This reduces jittering from left to right. We may be able

Просмотреть файл

@ -558,7 +558,7 @@ let ContentScroll = {
addMessageListener("Content:SetWindowSize", this);
if (Services.prefs.getBoolPref("layers.async-pan-zoom.enabled")) {
addEventListener("scroll", this, false);
addEventListener("scroll", this, true);
}
addEventListener("pagehide", this, false);
addEventListener("MozScrolledAreaChanged", this, false);

Просмотреть файл

@ -95,6 +95,15 @@
* @param aIgnoreScale ignore current scale factor.
* @return { x: converted x coordinate, y: converted y coordinate }
*
* rectClientToBrowser
* Convert a client Rect() in device pixels to page-relative
* coordinates in CSS pixels.
*
* @param aRect - client Rect to convert.
* @param aIgnoreScroll ignore root frame scroll.
* @param aIgnoreScale ignore current scale factor.
* @return converted Rect()
*
* ctobx, ctoby
* Convert individual x and y coordinates.
*
@ -172,6 +181,39 @@
</body>
</method>
<method name="rectClientToBrowser">
<parameter name="aClientRect"/>
<parameter name="aIgnoreScroll"/>
<parameter name="aIgnoreScale"/>
<body>
<![CDATA[
let ignoreScroll = aIgnoreScroll || false;
let ignoreScale = aIgnoreScale || false;
let scrollX = 0;
let scrollY = 0;
if (!ignoreScroll) {
let scroll = this.getRootView().getPosition();
scrollX = scroll.x;
scrollY = scroll.y;
}
let scale = 1;
if (!ignoreScale) {
scale = this.scale;
}
let bcr = this.getBoundingClientRect();
return new Rect(
(aClientRect.x + scrollX - bcr.left) / scale,
(aClientRect.y + scrollY - bcr.top) / scale,
aClientRect.width / scale,
aClientRect.height / scale
);
]]>
</body>
</method>
<method name="ctobx">
<parameter name="aX"/>
<parameter name="aIgnoreScroll"/>
@ -597,6 +639,18 @@
<field name="_frameLoader">null</field>
<field name="_contentViewManager">null</field>
<!--
* Returns the current content viewport bounds in browser coordinates
* taking into account zoom and scroll.
*
* @return Rect()
-->
<property name="contentViewportBounds">
<getter><![CDATA[
return this.rectClientToBrowser(this.getBoundingClientRect());
]]></getter>
</property>
<!-- Dimensions of content window -->
<field name="_contentWindowWidth">0</field>
<field name="_contentWindowHeight">0</field>
@ -798,9 +852,19 @@
onget="return this._contentTitle;"
readonly="true"/>
<property name="webNavigation"
onget="return this._remoteWebNavigation;"
readonly="true"/>
<field name="_remoteWebNavigation">null</field>
<property name="webNavigation" readonly="true">
<getter>
<![CDATA[
if (!this._remoteWebNavigation) {
let jsm = "resource://gre/modules/RemoteWebNavigation.jsm";
let RemoteWebNavigation = Components.utils.import(jsm, {}).RemoteWebNavigation;
this._remoteWebNavigation = new RemoteWebNavigation(this);
}
return this._remoteWebNavigation;
]]>
</getter>
</property>
<property name="contentWindow"
readonly="true"

Просмотреть файл

@ -164,14 +164,6 @@ Marker.prototype = {
},
position: function position(aX, aY) {
if (aX < 0) {
Util.dumpLn("Marker: aX is negative");
aX = 0;
}
if (aY < 0) {
Util.dumpLn("Marker: aY is negative");
aY = 0;
}
this._xPos = aX;
this._yPos = aY;
this._setPosition();
@ -254,7 +246,6 @@ var SelectionHelperUI = {
_caretMark: null,
_target: null,
_showAfterUpdate: false,
_movement: { active: false, x:0, y: 0 },
_activeSelectionRect: null,
_selectionMarkIds: [],
_targetIsEditable: false,
@ -358,13 +349,13 @@ var SelectionHelperUI = {
event.clientX, event.clientY);
break;
case "apzc-handle-pan-begin":
case "apzc-transform-begin":
if (this.isActive && this.layerMode == kContentLayer) {
this._hideMonocles();
}
break;
case "apzc-handle-pan-end":
case "apzc-transform-end":
// The selection range callback will check to see if the new
// position is off the screen, in which case it shuts down and
// clears the selection.
@ -549,8 +540,8 @@ var SelectionHelperUI = {
init: function () {
let os = Services.obs;
os.addObserver(this, "attach_edit_session_to_content", false);
os.addObserver(this, "apzc-handle-pan-begin", false);
os.addObserver(this, "apzc-handle-pan-end", false);
os.addObserver(this, "apzc-transform-begin", false);
os.addObserver(this, "apzc-transform-end", false);
},
_init: function _init(aMsgTarget) {
@ -582,8 +573,6 @@ var SelectionHelperUI = {
// bubble phase
window.addEventListener("click", this, false);
window.addEventListener("touchstart", this, false);
window.addEventListener("touchend", this, false);
window.addEventListener("touchmove", this, false);
Elements.browsers.addEventListener("URLChanged", this, true);
Elements.browsers.addEventListener("SizeChanged", this, true);
@ -603,15 +592,14 @@ var SelectionHelperUI = {
messageManager.removeMessageListener("Content:SelectionHandlerPong", this);
window.removeEventListener("keypress", this, true);
window.removeEventListener("click", this, false);
window.removeEventListener("touchstart", this, false);
window.removeEventListener("touchend", this, false);
window.removeEventListener("touchmove", this, false);
window.removeEventListener("MozPrecisePointer", this, true);
window.removeEventListener("MozDeckOffsetChanging", this, true);
window.removeEventListener("MozDeckOffsetChanged", this, true);
window.removeEventListener("KeyboardChanged", this, true);
window.removeEventListener("click", this, false);
window.removeEventListener("touchstart", this, false);
Elements.browsers.removeEventListener("URLChanged", this, true);
Elements.browsers.removeEventListener("SizeChanged", this, true);
@ -818,13 +806,31 @@ var SelectionHelperUI = {
_showMonocles: function _showMonocles(aSelection) {
if (!aSelection) {
this.caretMark.show();
if (this._checkMonocleVisibility(this.caretMark.xPos, this.caretMark.yPos)) {
this.caretMark.show();
}
} else {
this.endMark.show();
this.startMark.show();
if (this._checkMonocleVisibility(this.endMark.xPos, this.endMark.yPos)) {
this.endMark.show();
}
if (this._checkMonocleVisibility(this.startMark.xPos, this.startMark.yPos)) {
this.startMark.show();
}
}
},
_checkMonocleVisibility: function(aX, aY) {
let viewport = Browser.selectedBrowser.contentViewportBounds;
aX = this._msgTarget.ctobx(aX);
aY = this._msgTarget.ctoby(aY);
if (aX < viewport.x || aY < viewport.y ||
aX > (viewport.x + viewport.width) ||
aY > (viewport.y + viewport.height)) {
return false;
}
return true;
},
/*
* Event handlers for document events
*/
@ -926,16 +932,6 @@ var SelectionHelperUI = {
this._shutdown();
},
_checkMonocleVisibility: function(aX, aY) {
if (aX < 0 || aY < 0 ||
aX > ContentAreaObserver.viewableWidth ||
aY > ContentAreaObserver.viewableHeight) {
this.closeEditSession(true);
return false;
}
return true;
},
/*
* Message handlers
*/
@ -950,33 +946,25 @@ var SelectionHelperUI = {
if (json.updateStart) {
let x = this._msgTarget.btocx(json.start.xPos, true);
let y = this._msgTarget.btocx(json.start.yPos, true);
if (!this._checkMonocleVisibility(x, y)) {
return;
}
this.startMark.position(x, y);
}
if (json.updateEnd) {
let x = this._msgTarget.btocx(json.end.xPos, true);
let y = this._msgTarget.btocx(json.end.yPos, true);
if (!this._checkMonocleVisibility(x, y)) {
return;
}
this.endMark.position(x, y);
}
if (json.updateCaret) {
let x = this._msgTarget.btocx(json.caret.xPos, true);
let y = this._msgTarget.btocx(json.caret.yPos, true);
if (!this._checkMonocleVisibility(x, y)) {
return;
}
// If selectionRangeFound is set SelectionHelper found a range we can
// attach to. If not, there's no text in the control, and hence no caret
// position information we can use.
haveSelectionRect = json.selectionRangeFound;
if (json.selectionRangeFound) {
this.caretMark.position(x, y);
this.caretMark.show();
this._showMonocles(false);
}
}
@ -992,7 +980,7 @@ var SelectionHelperUI = {
this._targetElementRect =
this._msgTarget.rectBrowserToClient(json.element, true);
// Ifd this is the end of a selection move show the appropriate
// If this is the end of a selection move show the appropriate
// monocle images. src=(start, update, end, caret)
if (json.src == "start" || json.src == "end") {
this._showMonocles(true);
@ -1040,29 +1028,6 @@ var SelectionHelperUI = {
if (this._checkForActiveDrag()) {
aEvent.preventDefault();
}
let touch = aEvent.touches[0];
this._movement.x = touch.clientX;
this._movement.y = touch.clientY;
this._movement.active = true;
break;
}
case "touchend":
if (aEvent.touches.length == 0)
this._movement.active = false;
break;
case "touchmove": {
if (aEvent.touches.length != 1)
break;
let touch = aEvent.touches[0];
// Clear selection when the user pans the page
if (!this._checkForActiveDrag() && this._movement.active) {
if (Math.abs(touch.clientX - this._movement.x) > kDisableOnScrollDistance ||
Math.abs(touch.clientY - this._movement.y) > kDisableOnScrollDistance) {
this.closeEditSession(true);
}
}
break;
}

Просмотреть файл

@ -27,13 +27,6 @@ const kMaxVelocity = 6;
* prefs
*/
// A debug pref that when set makes us treat all precise pointer input
// as imprecise touch input. For debugging purposes only. Note there are
// subtle event sequencing differences in this feature when running on
// the desktop using the win32 widget backend and the winrt widget backend
// in metro. Fixing something in this mode does not insure the bug is
// in metro.
const kDebugMouseInputPref = "metro.debug.treatmouseastouch";
// Display rects around selection ranges. Useful in debugging
// selection problems.
const kDebugSelectionDisplayPref = "metro.debug.selection.displayRanges";
@ -104,18 +97,8 @@ var TouchModule = {
Services.obs.addObserver(this, "Gesture:SingleTap", false);
Services.obs.addObserver(this, "Gesture:DoubleTap", false);
try {
this._treatMouseAsTouch = Services.prefs.getBoolPref(kDebugMouseInputPref);
} catch (e) {}
},
/*
* Mouse input source tracking
*/
_treatMouseAsTouch: false,
/*
* Events
*/
@ -228,15 +211,6 @@ var TouchModule = {
},
_onContextMenu: function _onContextMenu(aEvent) {
// Special case when running on the desktop, fire off
// a edge ui event when we get the contextmenu event.
if (this._treatMouseAsTouch) {
let event = document.createEvent("Events");
event.initEvent("MozEdgeUICompleted", true, false);
window.dispatchEvent(event);
return;
}
// bug 598965 - chrome UI should stop to be pannable once the
// context menu has appeared.
if (ContextMenuUI.popupState) {

Просмотреть файл

@ -252,49 +252,6 @@ gTests.push({
},
});
gTests.push({
desc: "scroll disables",
setUp: setUpAndTearDown,
run: function test() {
sendContextMenuClick(100, 20);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
is(SelectionHelperUI.isActive, true, "selection active");
// scroll page
sendTouchDrag(gWindow,
400,
400,
400,
350);
yield waitForCondition(function () {
return !SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
// cancel fling from scroll above
TouchModule.cancelPending();
// active state - should be disabled after a page scroll
is(SelectionHelperUI.isActive, false, "selection inactive");
},
tearDown: function tearDown() {
EventUtils.synthesizeKey("VK_HOME", {}, gWindow);
emptyClipboard();
if (gWindow)
clearSelection(gWindow);
if (gFrame)
clearSelection(gFrame);
yield waitForCondition(function () {
return !SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
yield hideContextUI();
},
});
gTests.push({
desc: "tap on selection clears selection in content",
setUp: setUpAndTearDown,

Просмотреть файл

@ -26,7 +26,6 @@ pref("app.crashreporter.autosubmit", true);
pref("app.crashreporter.prompted", false);
// Debug prefs, see input.js
pref("metro.debug.treatmouseastouch", false);
pref("metro.debug.colorizeInputOverlay", false);
pref("metro.debug.selection.displayRanges", false);
pref("metro.debug.selection.dumpRanges", false);

Просмотреть файл

@ -595,11 +595,6 @@ documenttab[selected] .documenttab-selection {
padding: 0 !important;
}
#urlbar-edit > hbox > .textbox-input-box > .textbox-input::-moz-selection {
color: white;
background-color: @metro_orange@;
}
#urlbar-edit > hbox > .textbox-input-box > .textbox-input:invalid {
/* Hide error glow around the address bar that shows by default
* when URLs are made invalid by trmming. */

Просмотреть файл

@ -8,10 +8,19 @@
@namespace url("http://www.w3.org/1999/xhtml");
@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
/* Typography & General Styling -------------------------------------------- */
::-moz-selection {
color: white;
background-color: @metro_orange@;
}
*:-moz-any-link:focus {
outline-offset: -2px;
}
/* Input Styling -------------------------------------------- */
select:not([size]):not([multiple]) > xul|scrollbar,
select[size="1"] > xul|scrollbar,
select:not([size]):not([multiple]) xul|scrollbarbutton,

Просмотреть файл

@ -11,6 +11,11 @@
/* Typography & General Styling -------------------------------------------- */
::-moz-selection {
color: white;
background-color: @metro_orange@;
}
:root {
font-family: "Segoe UI", sans-serif !important;
font-size: @font_normal@;

Просмотреть файл

@ -992,7 +992,7 @@ bool AsyncPanZoomController::DoFling(const TimeDuration& aDelta) {
if (!shouldContinueFlingX && !shouldContinueFlingY) {
SendAsyncScrollEvent();
RequestContentRepaint();
mState = NOTHING;
SetState(NOTHING);
return false;
}
@ -1016,8 +1016,7 @@ bool AsyncPanZoomController::DoFling(const TimeDuration& aDelta) {
}
void AsyncPanZoomController::CancelAnimation() {
ReentrantMonitorAutoEnter lock(mMonitor);
mState = NOTHING;
SetState(NOTHING);
}
void AsyncPanZoomController::SetCompositorParent(CompositorParent* aCompositorParent) {
@ -1268,7 +1267,7 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
requestAnimationFrame = true;
if (aSampleTime - mAnimationStartTime >= ZOOM_TO_DURATION) {
mState = NOTHING;
SetState(NOTHING);
SendAsyncScrollEvent();
RequestContentRepaint();
}
@ -1376,7 +1375,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
mY.CancelTouch();
mFrameMetrics = aLayerMetrics;
mState = NOTHING;
SetState(NOTHING);
} else {
// If we're not taking the aLayerMetrics wholesale we still need to pull
// in some things into our local mFrameMetrics because these things are
@ -1554,14 +1553,18 @@ void AsyncPanZoomController::SetState(PanZoomState aNewState) {
}
if (mGeckoContentController) {
if (IsPanningState(oldState) && !IsPanningState(aNewState)) {
mGeckoContentController->HandlePanEnd();
} else if (!IsPanningState(oldState) && IsPanningState(aNewState)) {
mGeckoContentController->HandlePanBegin();
if (!IsTransformingState(oldState) && IsTransformingState(aNewState)) {
mGeckoContentController->NotifyTransformBegin();
} else if (IsTransformingState(oldState) && !IsTransformingState(aNewState)) {
mGeckoContentController->NotifyTransformEnd();
}
}
}
bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) {
return !(aState == NOTHING || aState == TOUCHING || aState == WAITING_LISTENERS);
}
bool AsyncPanZoomController::IsPanningState(PanZoomState aState) {
return (aState == PANNING || aState == PANNING_LOCKED_X || aState == PANNING_LOCKED_Y);
}

Просмотреть файл

@ -510,6 +510,19 @@ private:
prevented the default actions yet. we still need to abort animations. */
};
/**
* Helper to set the current state. Holds the monitor before actually setting
* it and fires content controller events based on state changes. Always set
* the state using this call, do not set it directly.
*/
void SetState(PanZoomState aState);
/**
* Internal helpers for checking general state of this apzc.
*/
bool IsTransformingState(PanZoomState aState);
bool IsPanningState(PanZoomState mState);
enum AxisLockMode {
FREE, /* No locking at all */
STANDARD, /* Default axis locking mode that remains locked until pan ends*/
@ -518,15 +531,6 @@ private:
static AxisLockMode GetAxisLockMode();
/**
* Helper to set the current state. Holds the monitor before actually setting
* it. If the monitor is already held by the current thread, it is safe to
* instead use: |mState = NEWSTATE;|
*/
void SetState(PanZoomState aState);
bool IsPanningState(PanZoomState mState);
uint64_t mLayersId;
nsRefPtr<CompositorParent> mCompositorParent;
TaskThrottler mPaintThrottler;

Просмотреть файл

@ -77,14 +77,12 @@ public:
}
/**
* Request any special actions be performed when panning starts
* General tranformation notices for consumers. These fire any time
* the apzc is modifying the view, including panning, zooming, and
* fling.
*/
virtual void HandlePanBegin() {}
/**
* Request any special actions be performed when panning ends
*/
virtual void HandlePanEnd() {}
virtual void NotifyTransformBegin() {}
virtual void NotifyTransformEnd() {}
GeckoContentController() {}
virtual ~GeckoContentController() {}

Просмотреть файл

@ -668,6 +668,20 @@ public class ActivityChooserModel extends DataSetObservable {
}
}
public int getDistinctActivityCountInHistory() {
synchronized (mInstanceLock) {
ensureConsistentState();
final List<String> packages = new ArrayList<String>();
for (HistoricalRecord record : mHistoricalRecords) {
String activity = record.activity.flattenToString();
if (!packages.contains(activity)) {
packages.add(activity);
}
}
return packages.size();
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();

Просмотреть файл

@ -54,7 +54,7 @@ public class GeckoActionProvider extends ActionProvider {
view.setActionButtonClickListener(mCallbacks);
final PackageManager packageManager = mContext.getPackageManager();
int historySize = dataModel.getHistorySize();
int historySize = dataModel.getDistinctActivityCountInHistory();
if (historySize > 2) {
historySize = 2;
}
@ -142,4 +142,3 @@ public class GeckoActionProvider extends ActionProvider {
}
}
}

Просмотреть файл

@ -474,8 +474,10 @@ AbstractFile.writeAtomic =
*/
AbstractFile.removeDir = function(path, options = {}) {
let iterator = new OS.File.DirectoryIterator(path);
if (!iterator.exists() && options.ignoreAbsent) {
return;
if (!iterator.exists()) {
if (!("ignoreAbsent" in options) || options.ignoreAbsent) {
return;
}
}
try {

Просмотреть файл

@ -36,7 +36,7 @@ add_task(function() {
// Remove non-existent directory
let exception = null;
try {
yield OS.File.removeDir(dir);
yield OS.File.removeDir(dir, {ignoreAbsent: false});
} catch (ex) {
exception = ex;
}
@ -46,12 +46,13 @@ add_task(function() {
// Remove non-existent directory with ignoreAbsent
yield OS.File.removeDir(dir, {ignoreAbsent: true});
yield OS.File.removeDir(dir);
// Remove file
// Remove file with ignoreAbsent: false
yield OS.File.writeAtomic(file, "content", { tmpPath: file + ".tmp" });
exception = null;
try {
yield OS.File.removeDir(file);
yield OS.File.removeDir(file, {ignoreAbsent: false});
} catch (ex) {
exception = ex;
}
@ -67,15 +68,14 @@ add_task(function() {
// Remove directory that contains one file
yield OS.File.makeDir(dir);
yield OS.File.writeAtomic(file1, "content", { tmpPath: file1 + ".tmp" });
//yield OS.File.open(file1, {create:true});
yield OS.File.removeDir(dir)
yield OS.File.removeDir(dir);
do_check_false((yield OS.File.exists(dir)));
// Remove directory that contains multiple files
yield OS.File.makeDir(dir);
yield OS.File.writeAtomic(file1, "content", { tmpPath: file1 + ".tmp" });
yield OS.File.writeAtomic(file2, "content", { tmpPath: file2 + ".tmp" });
yield OS.File.removeDir(dir)
yield OS.File.removeDir(dir);
do_check_false((yield OS.File.exists(dir)));
// Remove directory that contains a file and a directory

Просмотреть файл

@ -2831,6 +2831,13 @@
"extended_statistics_ok": true,
"description": "Session restore: Duration of the longest uninterruptible operation while writing session data (ms)"
},
"FX_SESSION_RESTORE_FILE_SIZE_BYTES": {
"kind": "exponential",
"high": 50000000,
"n_buckets": 30,
"extended_statistics_ok": true,
"description": "Session restore: The size of file sessionstore.js (bytes)"
},
"FX_SESSION_RESTORE_CORRUPT_FILE": {
"kind": "boolean",
"description": "Session restore: Whether the file read on startup contained parse-able JSON"

Просмотреть файл

@ -227,6 +227,13 @@ WebConsoleActor.prototype =
*/
consoleReflowListener: null,
/**
* The JSTerm Helpers names cache.
* @private
* @type array
*/
_jstermHelpersCache: null,
/**
* Getter for the NetworkMonitor.saveRequestAndResponseBodies preference.
* @type boolean
@ -692,12 +699,29 @@ WebConsoleActor.prototype =
*/
onAutocomplete: function WCA_onAutocomplete(aRequest)
{
// TODO: Bug 842682 - use the debugger API for autocomplete in the Web
// Console, and provide suggestions from the selected debugger stack frame.
// Also, properly reuse _getJSTermHelpers instead of re-implementing it
// here.
let result = JSPropertyProvider(this.window, aRequest.text,
aRequest.cursor) || {};
let frameActorId = aRequest.frameActor;
let dbgObject = null;
let environment = null;
// This is the case of the paused debugger
if (frameActorId) {
let frameActor = this.conn.getActor(frameActorId);
if (frameActor) {
let frame = frameActor.frame;
environment = frame.environment;
}
else {
Cu.reportError("Web Console Actor: the frame actor was not found: " +
frameActorId);
}
}
// This is the general case (non-paused debugger)
else {
dbgObject = this.dbg.makeGlobalObjectReference(this.window);
}
let result = JSPropertyProvider(dbgObject, environment, aRequest.text,
aRequest.cursor, frameActorId) || {};
let matches = result.matches || [];
let reqText = aRequest.text.substr(0, aRequest.cursor);
@ -705,13 +729,14 @@ WebConsoleActor.prototype =
// helper functions.
let lastNonAlphaIsDot = /[.][a-zA-Z0-9$]*$/.test(reqText);
if (!lastNonAlphaIsDot) {
let helpers = {
sandbox: Object.create(null)
};
JSTermHelpers(helpers);
let helperNames = Object.getOwnPropertyNames(helpers.sandbox);
matches = matches.concat(helperNames.filter(n => n.startsWith(result.matchProp)));
if (!this._jstermHelpersCache) {
let helpers = {
sandbox: Object.create(null)
};
JSTermHelpers(helpers);
this._jstermHelpersCache = Object.getOwnPropertyNames(helpers.sandbox);
}
matches = matches.concat(this._jstermHelpersCache.filter(n => n.startsWith(result.matchProp)));
}
return {

Просмотреть файл

@ -15,16 +15,10 @@ const { method, Arg, Option, RetVal } = protocol;
const WEBGL_CONTEXT_NAMES = ["webgl", "experimental-webgl", "moz-webgl"];
const HIGHLIGHT_FRAG_SHADER = [
"precision lowp float;",
"void main() {",
"gl_FragColor.rgba = vec4(%color);",
"}"
].join("\n");
// These traits are bit masks. Make sure they're powers of 2.
const PROGRAM_DEFAULT_TRAITS = 0;
const PROGRAM_BLACKBOX_TRAIT = 1;
const PROGRAM_HIGHLIGHT_TRAIT = 2;
exports.register = function(handle) {
handle.addTabActor(WebGLActor, "webglActor");
@ -160,26 +154,21 @@ let ProgramActor = protocol.ActorClass({
}),
/**
* Replaces this program's fragment shader with an temporary
* easy-to-distinguish alternative. See HIGHLIGHT_FRAG_SHADER.
* Highlights any geometry rendered using this program.
*/
highlight: method(function(color) {
let shaderActor = this._getShaderActor("fragment");
let oldText = shaderActor.text;
let newText = HIGHLIGHT_FRAG_SHADER.replace("%color", color)
shaderActor.compile(newText);
shaderActor.text = oldText;
highlight: method(function(tint) {
this.linkedProxy.highlightTint = tint;
this.linkedCache.setProgramTrait(this.program, PROGRAM_HIGHLIGHT_TRAIT);
}, {
request: { color: Arg(0, "array:string") },
request: { tint: Arg(0, "array:number") },
oneway: true
}),
/**
* Reverts this program's fragment shader to the latest user-defined source.
* Allows geometry to be rendered normally using this program.
*/
unhighlight: method(function() {
let shaderActor = this._getShaderActor("fragment");
shaderActor.compile(shaderActor.text);
this.linkedCache.unsetProgramTrait(this.program, PROGRAM_HIGHLIGHT_TRAIT);
}, {
oneway: true
}),
@ -477,29 +466,31 @@ let WebGLInstrumenter = {
* The targeted WebGL context instance.
* @param string funcName
* The function to override.
* @param string callbackName [optional]
* A custom callback function name in the observer. If unspecified,
* it will default to the name of the function to override.
* @param array callbackName [optional]
* The two callback function names in the observer, corresponding to
* the "before" and "after" invocation times. If unspecified, they will
* default to the name of the function to override.
* @param number timing [optional]
* When to issue the callback in relation to the actual context
* function call. Availalble values are 0 for "before" (default)
* and 1 for "after".
* function call. Availalble values are -1 for "before" (default)
* 1 for "after" and 0 for "before and after".
*/
_instrument: function(observer, context, funcName, callbackName, timing = 0) {
_instrument: function(observer, context, funcName, callbackName = [], timing = -1) {
let { cache, proxy } = observer.for(context);
let originalFunc = context[funcName];
let proxyFuncName = callbackName || funcName;
let beforeFuncName = callbackName[0] || funcName;
let afterFuncName = callbackName[1] || callbackName[0] || funcName;
context[funcName] = function(...glArgs) {
if (timing == 0 && !observer.suppressHandlers) {
let glBreak = observer[proxyFuncName](glArgs, cache, proxy);
if (timing <= 0 && !observer.suppressHandlers) {
let glBreak = observer[beforeFuncName](glArgs, cache, proxy);
if (glBreak) return undefined;
}
let glResult = originalFunc.apply(this, glArgs);
if (timing == 1 && !observer.suppressHandlers) {
let glBreak = observer[proxyFuncName](glArgs, glResult, cache, proxy);
if (timing >= 0 && !observer.suppressHandlers) {
let glBreak = observer[afterFuncName](glArgs, glResult, cache, proxy);
if (glBreak) return undefined;
}
@ -516,19 +507,28 @@ let WebGLInstrumenter = {
"linkProgram", "getAttribLocation", "getUniformLocation"
]
}, {
callback: "toggleVertexAttribArray",
timing: -1, // before
callback: [
"toggleVertexAttribArray"
],
functions: [
"enableVertexAttribArray", "disableVertexAttribArray"
]
}, {
callback: "attribute_",
timing: -1, // before
callback: [
"attribute_"
],
functions: [
"vertexAttrib1f", "vertexAttrib2f", "vertexAttrib3f", "vertexAttrib4f",
"vertexAttrib1fv", "vertexAttrib2fv", "vertexAttrib3fv", "vertexAttrib4fv",
"vertexAttribPointer"
]
}, {
callback: "uniform_",
timing: -1, // before
callback: [
"uniform_"
],
functions: [
"uniform1i", "uniform2i", "uniform3i", "uniform4i",
"uniform1f", "uniform2f", "uniform3f", "uniform4f",
@ -537,10 +537,17 @@ let WebGLInstrumenter = {
"uniformMatrix2fv", "uniformMatrix3fv", "uniformMatrix4fv"
]
}, {
timing: 1, // after
functions: ["useProgram"]
timing: -1, // before
functions: [
"useProgram", "enable", "disable", "blendColor",
"blendEquation", "blendEquationSeparate",
"blendFunc", "blendFuncSeparate"
]
}, {
callback: "draw_",
timing: 0, // before and after
callback: [
"beforeDraw_", "afterDraw_"
],
functions: [
"drawArrays", "drawElements"
]
@ -573,6 +580,7 @@ WebGLObserver.prototype = {
registerContextForWindow: function(id, context) {
let cache = new WebGLCache(id, context);
let proxy = new WebGLProxy(id, context, cache, this);
cache.refreshState(proxy);
this._contexts.set(context, {
ownerWindow: id,
@ -706,21 +714,116 @@ WebGLObserver.prototype = {
},
/**
* Called immediately *after* 'useProgram' is requested in the context.
* Called immediately *before* 'useProgram' is requested in the context.
*
* @param array glArgs
* Overridable arguments with which the function is called.
* @param void glResult
* The returned value of the original function call.
* @param WebGLCache cache
* The state storage for the WebGL context initiating this call.
*/
useProgram: function(glArgs, glResult, cache) {
useProgram: function(glArgs, cache) {
// Manually keeping a cache and not using gl.getParameter(CURRENT_PROGRAM)
// because gl.get* functions are slow as potatoes.
cache.currentProgram = glArgs[0];
},
/**
* Called immediately *before* 'enable' is requested in the context.
*
* @param array glArgs
* Overridable arguments with which the function is called.
* @param WebGLCache cache
* The state storage for the WebGL context initiating this call.
*/
enable: function(glArgs, cache) {
cache.currentState[glArgs[0]] = true;
},
/**
* Called immediately *before* 'disable' is requested in the context.
*
* @param array glArgs
* Overridable arguments with which the function is called.
* @param WebGLCache cache
* The state storage for the WebGL context initiating this call.
*/
disable: function(glArgs, cache) {
cache.currentState[glArgs[0]] = false;
},
/**
* Called immediately *before* 'blendColor' is requested in the context.
*
* @param array glArgs
* Overridable arguments with which the function is called.
* @param WebGLCache cache
* The state storage for the WebGL context initiating this call.
*/
blendColor: function(glArgs, cache) {
let blendColor = cache.currentState.blendColor;
blendColor[0] = glArgs[0];
blendColor[1] = glArgs[1];
blendColor[2] = glArgs[2];
blendColor[3] = glArgs[3];
},
/**
* Called immediately *before* 'blendEquation' is requested in the context.
*
* @param array glArgs
* Overridable arguments with which the function is called.
* @param WebGLCache cache
* The state storage for the WebGL context initiating this call.
*/
blendEquation: function(glArgs, cache) {
let state = cache.currentState;
state.blendEquationRgb = state.blendEquationAlpha = glArgs[0];
},
/**
* Called immediately *before* 'blendEquationSeparate' is requested in the context.
*
* @param array glArgs
* Overridable arguments with which the function is called.
* @param WebGLCache cache
* The state storage for the WebGL context initiating this call.
*/
blendEquationSeparate: function(glArgs, cache) {
let state = cache.currentState;
state.blendEquationRgb = glArgs[0];
state.blendEquationAlpha = glArgs[1];
},
/**
* Called immediately *before* 'blendFunc' is requested in the context.
*
* @param array glArgs
* Overridable arguments with which the function is called.
* @param WebGLCache cache
* The state storage for the WebGL context initiating this call.
*/
blendFunc: function(glArgs, cache) {
let state = cache.currentState;
state.blendSrcRgb = state.blendSrcAlpha = glArgs[0];
state.blendDstRgb = state.blendDstAlpha = glArgs[1];
},
/**
* Called immediately *before* 'blendFuncSeparate' is requested in the context.
*
* @param array glArgs
* Overridable arguments with which the function is called.
* @param WebGLCache cache
* The state storage for the WebGL context initiating this call.
*/
blendFuncSeparate: function(glArgs, cache) {
let state = cache.currentState;
state.blendSrcRgb = glArgs[0];
state.blendDstRgb = glArgs[1];
state.blendSrcAlpha = glArgs[2];
state.blendDstAlpha = glArgs[3];
},
/**
* Called immediately *before* 'drawArrays' or 'drawElements' is requested
* in the context.
@ -729,10 +832,44 @@ WebGLObserver.prototype = {
* Overridable arguments with which the function is called.
* @param WebGLCache cache
* The state storage for the WebGL context initiating this call.
* @param WebGLProxy proxy
* The proxy methods for the WebGL context initiating this call.
*/
draw_: function(glArgs, cache) {
// Return true to break original function call.
return cache.currentProgramTraits & PROGRAM_BLACKBOX_TRAIT;
beforeDraw_: function(glArgs, cache, proxy) {
let traits = cache.currentProgramTraits;
// Handle program blackboxing.
if (traits & PROGRAM_BLACKBOX_TRAIT) {
return true; // Return true to break original function call.
}
// Handle program highlighting.
if (traits & PROGRAM_HIGHLIGHT_TRAIT) {
proxy.enableHighlighting();
}
return false;
},
/**
* Called immediately *after* 'drawArrays' or 'drawElements' is requested
* in the context.
*
* @param array glArgs
* Overridable arguments with which the function is called.
* @param void glResult
* The returned value of the original function call.
* @param WebGLCache cache
* The state storage for the WebGL context initiating this call.
* @param WebGLProxy proxy
* The proxy methods for the WebGL context initiating this call.
*/
afterDraw_: function(glArgs, glResult, cache, proxy) {
let traits = cache.currentProgramTraits;
// Handle program highlighting.
if (traits & PROGRAM_HIGHLIGHT_TRAIT) {
proxy.disableHighlighting();
}
}
};
@ -749,6 +886,7 @@ function WebGLCache(id, context) {
this._id = id;
this._gl = context;
this._programs = new Map();
this.currentState = {};
}
WebGLCache.prototype = {
@ -762,6 +900,35 @@ WebGLCache.prototype = {
get ownerWindow() this._id,
get ownerContext() this._gl,
/**
* A collection of flags or properties representing the context's state.
* Implemented as an object hash and not a Map instance because keys are
* always either strings or numbers.
*/
currentState: null,
/**
* Populates the current state with values retrieved from the context.
*
* @param WebGLProxy proxy
* The proxy methods for the WebGL context owning the state.
*/
refreshState: function(proxy) {
let gl = this._gl;
let s = this.currentState;
// Populate only with the necessary parameters. Not all default WebGL
// state values are required.
s[gl.BLEND] = proxy.isEnabled("BLEND");
s.blendColor = proxy.getParameter("BLEND_COLOR");
s.blendEquationRgb = proxy.getParameter("BLEND_EQUATION_RGB");
s.blendEquationAlpha = proxy.getParameter("BLEND_EQUATION_ALPHA");
s.blendSrcRgb = proxy.getParameter("BLEND_SRC_RGB");
s.blendSrcAlpha = proxy.getParameter("BLEND_SRC_ALPHA");
s.blendDstRgb = proxy.getParameter("BLEND_DST_RGB");
s.blendDstAlpha = proxy.getParameter("BLEND_DST_ALPHA");
},
/**
* Adds a program to the cache.
*
@ -947,10 +1114,14 @@ function WebGLProxy(id, context, cache, observer) {
this._observer = observer;
let exports = [
"isEnabled",
"getParameter",
"getAttachedShaders",
"getShaderSource",
"getShaderOfType",
"compileShader"
"compileShader",
"enableHighlighting",
"disableHighlighting"
];
exports.forEach(e => this[e] = (...args) => this._call(e, args));
}
@ -964,6 +1135,64 @@ WebGLProxy.prototype = {
get ownerWindow() this._id,
get ownerContext() this._gl,
/**
* Test whether a WebGL capability is enabled.
*
* @param string name
* The WebGL capability name, for example "BLEND".
* @return boolean
* True if enabled, false otherwise.
*/
_isEnabled: function(name) {
return this._gl.isEnabled(this._gl[name]);
},
/**
* Returns the value for the specified WebGL parameter name.
*
* @param string name
* The WebGL parameter name, for example "BLEND_COLOR".
* @return any
* The corresponding parameter's value.
*/
_getParameter: function(name) {
return this._gl.getParameter(this._gl[name]);
},
/**
* Returns the renderbuffer property value for the specified WebGL parameter.
* If no renderbuffer binding is available, null is returned.
*
* @param string name
* The WebGL parameter name, for example "BLEND_COLOR".
* @return any
* The corresponding parameter's value.
*/
_getRenderbufferParameter: function(name) {
if (!this._getParameter("RENDERBUFFER_BINDING")) {
return null;
}
let gl = this._gl;
return gl.getRenderbufferParameter(gl.RENDERBUFFER, gl[name]);
},
/**
* Returns the framebuffer property value for the specified WebGL parameter.
* If no framebuffer binding is available, null is returned.
*
* @param string name
* The WebGL parameter name, for example "BLEND_COLOR".
* @return any
* The corresponding parameter's value.
*/
_getFramebufferAttachmentParameter: function(type, name) {
if (!this._getParameter("FRAMEBUFFER_BINDING")) {
return null;
}
let gl = this._gl;
return gl.getFramebufferAttachmentParameter(gl.RENDERBUFFER, gl[type], gl[name]);
},
/**
* Returns the shader objects attached to a program object.
*
@ -1046,6 +1275,48 @@ WebGLProxy.prototype = {
return error;
},
/**
* Enables color blending based on the geometry highlight tint.
*/
_enableHighlighting: function() {
let gl = this._gl;
// Avoid changing the blending params when rendering to a depth texture.
let format = this._getRenderbufferParameter("RENDERBUFFER_INTERNAL_FORMAT");
if (format == gl.DEPTH_COMPONENT16) {
return;
}
// Non-premultiplied alpha blending based on a predefined constant color.
// Simply using gl.colorMask won't work, because we want non-tinted colors
// to be drawn as black, not ignored.
gl.enable(gl.BLEND);
gl.blendColor.apply(gl, this.highlightTint);
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.CONSTANT_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.CONSTANT_COLOR, gl.ZERO);
this.wasHighlighting = true;
},
/**
* Disables color blending based on the geometry highlight tint, by
* reverting the corresponding params back to their original values.
*/
_disableHighlighting: function() {
let gl = this._gl;
let s = this._cache.currentState;
gl[s[gl.BLEND] ? "enable" : "disable"](gl.BLEND);
gl.blendColor.apply(gl, s.blendColor);
gl.blendEquationSeparate(s.blendEquationRgb, s.blendEquationAlpha);
gl.blendFuncSeparate(s.blendSrcRgb, s.blendDstRgb, s.blendSrcAlpha, s.blendDstAlpha);
},
/**
* The color tint used for highlighting geometry.
* @see _enableHighlighting and _disableHighlighting.
*/
highlightTint: [0, 0, 0, 0],
/**
* Executes a function in this object.
*

Просмотреть файл

@ -121,14 +121,17 @@ WebConsoleClient.prototype = {
* Cursor location inside the string. Index starts from 0.
* @param function aOnResponse
* The function invoked when the response is received.
* @param string aFrameActor
* The id of the frame actor that made the call.
*/
autocomplete: function WCC_autocomplete(aString, aCursor, aOnResponse)
autocomplete: function WCC_autocomplete(aString, aCursor, aOnResponse, aFrameActor)
{
let packet = {
to: this._actor,
type: "autocomplete",
text: aString,
cursor: aCursor,
frameActor: aFrameActor,
};
this._client.request(packet, aOnResponse);
},

Просмотреть файл

@ -741,10 +741,14 @@ function findCompletionBeginning(aStr)
/**
* Provides a list of properties, that are possible matches based on the passed
* scope and inputValue.
* Debugger.Environment/Debugger.Object and inputValue.
*
* @param object aScope
* Scope to use for the completion.
* @param object aDbgObject
* When the debugger is not paused this Debugger.Object wraps the scope for autocompletion.
* It is null if the debugger is paused.
* @param object anEnvironment
* When the debugger is paused this Debugger.Environment is the scope for autocompletion.
* It is null if the debugger is not paused.
* @param string aInputValue
* Value that should be completed.
* @param number [aCursor=aInputValue.length]
@ -760,14 +764,13 @@ function findCompletionBeginning(aStr)
* the matches-strings.
* }
*/
function JSPropertyProvider(aScope, aInputValue, aCursor)
function JSPropertyProvider(aDbgObject, anEnvironment, aInputValue, aCursor)
{
if (aCursor === undefined) {
aCursor = aInputValue.length;
}
let inputValue = aInputValue.substring(0, aCursor);
let obj = WCU.unwrap(aScope);
// Analyse the inputValue and find the beginning of the last part that
// should be completed.
@ -799,69 +802,221 @@ function JSPropertyProvider(aScope, aInputValue, aCursor)
(completionPart[0] == "'" || completionPart[0] == '"') &&
completionPart[lastDot - 1] == completionPart[0]) {
// We are completing a string literal.
obj = obj.String.prototype;
let obj = String.prototype;
matchProp = completionPart.slice(lastDot + 1);
let matches = Object.keys(getMatchedProps(obj, {matchProp:matchProp}));
return {
matchProp: matchProp,
matches: matches,
};
}
else {
// We are completing a variable / a property lookup.
let properties = completionPart.split(".");
if (properties.length > 1) {
matchProp = properties.pop().trimLeft();
for (let i = 0; i < properties.length; i++) {
let obj;
//The first property must be found in the environment or the Debugger.Object
//depending of whether the debugger is paused or not
let prop = properties[0];
if (anEnvironment) {
obj = getVariableInEnvironment(anEnvironment, prop);
}
else {
obj = getPropertyInDebuggerObject(aDbgObject, prop);
}
if (obj == null) {
return null;
}
//We get the rest of the properties recursively starting from the Debugger.Object
// that wraps the first property
for (let i = 1; i < properties.length; i++) {
let prop = properties[i].trim();
if (!prop) {
return null;
}
obj = getPropertyInDebuggerObject(obj, prop);
// If obj is undefined or null (which is what "== null" does),
// then there is no chance to run completion on it. Exit here.
if (obj == null) {
return null;
}
// Check if prop is a getter function on obj. Functions can change other
// stuff so we can't execute them to get the next object. Stop here.
if (WCU.isNonNativeGetter(obj, prop)) {
return null;
}
try {
obj = obj[prop];
}
catch (ex) {
return null;
}
}
// If the final property is a primitive
if (typeof obj != 'object' || obj === null) {
matchProp = completionPart.slice(lastDot + 1);
let matches = Object.keys(getMatchedProps(obj, {matchProp:matchProp}));
return {
matchProp: matchProp,
matches: matches,
};
}
return getMatchedPropsInDbgObject(obj, matchProp);
}
else {
matchProp = properties[0].trimLeft();
}
if (anEnvironment) {
return getMatchedPropsInEnvironment(anEnvironment, matchProp);
}
else {
if (typeof aDbgObject != 'object' || aDbgObject === null) {
matchProp = completionPart.slice(lastDot + 1);
let matches = Object.keys(getMatchedProps(aDbgObject, {matchProp:matchProp}));
// If obj is undefined or null (which is what "== null" does),
// then there is no chance to run completion on it. Exit here.
if (obj == null) {
return null;
return {
matchProp: matchProp,
matches: matches,
};
}
return getMatchedPropsInDbgObject(aDbgObject, matchProp);
}
}
}
}
/**
* Returns the value of aProp in anEnvironment as a debuggee value, by recursively checking the environment chain
*
* @param object anEnvironment
* A Debugger.Environment to look the aProp into.
* @param string aProp
* The property that is looked up.
* @returns null or object
* A Debugger.Object if aProp exists in the environment chain, null otherwise.
*/
function getVariableInEnvironment(anEnvironment, aProp)
{
for (let env = anEnvironment; env; env = env.parent) {
try {
// Skip Iterators and Generators.
if (WCU.isIteratorOrGenerator(obj)) {
return null;
let obj = env.getVariable(aProp);
if (obj) {
return obj;
}
}
catch (ex) {
// The above can throw if |obj| is a dead object.
// TODO: we should use Cu.isDeadWrapper() - see bug 885800.
return null;
}
}
return null;
}
let matches = Object.keys(getMatchedProps(obj, {matchProp:matchProp}));
/**
* Returns the value of aProp in aDbgObject as a debuggee value, by recursively checking the prototype chain
*
* @param object aDbgObject
* A Debugger.Object to look the aProp into.
* @param string aProp
* The property that is looked up.
* @returns null or object
* A Debugger.Object if aProp exists in the prototype chain, null otherwise.
*/
function getPropertyInDebuggerObject(aDbgObject, aProp)
{
let dbgObject = aDbgObject;
while (dbgObject) {
try {
let desc = dbgObject.getOwnPropertyDescriptor(aProp)
if (desc) {
let obj = desc.value;
if (obj)
return obj;
obj = desc.get;
if (obj)
return obj;
}
dbgObject = dbgObject.proto;
}
catch (ex) {
return null;
}
}
return null;
}
/**
* Get all properties on the given Debugger.Environment (and its parent chain) that match a given prefix.
*
* @param Debugger.Environment anEnvironment
* Debugger.Environment whose properties we want to filter.
*
* @param string matchProp Filter for properties that match this one.
*
* @return object
* Object that contains the matchProp and the list of names.
*/
function getMatchedPropsInEnvironment(anEnvironment, matchProp)
{
let names = Object.create(null);
let c = MAX_COMPLETIONS;
for (let env = anEnvironment; env; env = env.parent) {
let ownNames = env.names();
for (let i = 0; i < ownNames.length; i++) {
if (ownNames[i].indexOf(matchProp) != 0 ||
ownNames[i] in names) {
continue;
}
c--;
if (c < 0) {
return {
matchProp: matchProp,
matches: Object.keys(names)
};
}
names[ownNames[i]] = true;
}
}
return {
matchProp: matchProp,
matches: matches,
matches: Object.keys(names)
};
}
/**
* Get all properties on the given Debugger.Object (and the prototype chain of the wrapped value) that match a given prefix.
*
* @param Debugger.Object aDbgObject
* Debugger.Object whose properties we want to filter.
*
* @param string matchProp Filter for properties that match this one.
*
* @return object
* Object that contains the matchProp and the list of names.
*/
function getMatchedPropsInDbgObject(aDbgObject, matchProp)
{
let names = Object.create(null);
let c = MAX_COMPLETIONS;
for (let dbg = aDbgObject; dbg; dbg = dbg.proto) {
let raw = dbg.unsafeDereference();
if (Cu.isDeadWrapper(raw)) {
return null;
}
let ownNames = dbg.getOwnPropertyNames();
for (let i = 0; i < ownNames.length; i++) {
if (ownNames[i].indexOf(matchProp) != 0 ||
ownNames[i] in names) {
continue;
}
c--;
if (c < 0) {
return {
matchProp: matchProp,
matches: Object.keys(names)
};
}
names[ownNames[i]] = true;
}
}
return {
matchProp: matchProp,
matches: Object.keys(names)
};
}

Просмотреть файл

@ -2279,6 +2279,8 @@ this.AddonManager = {
UPDATE_STATUS_UNKNOWN_FORMAT: -4,
// The update information was not correctly signed or there was an SSL error.
UPDATE_STATUS_SECURITY_ERROR: -5,
// The update was cancelled.
UPDATE_STATUS_CANCELLED: -6,
// Constants to indicate why an update check is being performed
// Update check has been requested by the user.

Просмотреть файл

@ -490,8 +490,14 @@ UpdateParser.prototype = {
this.notifyError(AddonUpdateChecker.ERROR_PARSE_ERROR);
return;
}
if ("onUpdateCheckComplete" in this.observer)
this.observer.onUpdateCheckComplete(results);
if ("onUpdateCheckComplete" in this.observer) {
try {
this.observer.onUpdateCheckComplete(results);
}
catch (e) {
WARN("onUpdateCheckComplete notification failed", e);
}
}
return;
}
@ -534,8 +540,14 @@ UpdateParser.prototype = {
* Helper method to notify the observer that an error occured.
*/
notifyError: function UP_notifyError(aStatus) {
if ("onUpdateCheckError" in this.observer)
this.observer.onUpdateCheckError(aStatus);
if ("onUpdateCheckError" in this.observer) {
try {
this.observer.onUpdateCheckError(aStatus);
}
catch (e) {
WARN("onUpdateCheckError notification failed", e);
}
}
},
/**
@ -549,6 +561,17 @@ UpdateParser.prototype = {
WARN("Request timed out");
this.notifyError(AddonUpdateChecker.ERROR_TIMEOUT);
},
/**
* Called to cancel an in-progress update check.
*/
cancel: function UP_cancel() {
this.timer.cancel();
this.timer = null;
this.request.abort();
this.request = null;
this.notifyError(AddonUpdateChecker.ERROR_CANCELLED);
}
};
@ -610,6 +633,8 @@ this.AddonUpdateChecker = {
ERROR_UNKNOWN_FORMAT: -4,
// The update information was not correctly signed or there was an SSL error.
ERROR_SECURITY_ERROR: -5,
// The update was cancelled
ERROR_CANCELLED: -6,
/**
* Retrieves the best matching compatibility update for the application from
@ -721,9 +746,11 @@ this.AddonUpdateChecker = {
* The URL of the add-on's update manifest
* @param aObserver
* An observer to notify of results
* @return UpdateParser so that the caller can use UpdateParser.cancel() to shut
* down in-progress update requests
*/
checkForUpdates: function AUC_checkForUpdates(aId, aUpdateKey, aUrl,
aObserver) {
new UpdateParser(aId, aUpdateKey, aUrl, aObserver);
return new UpdateParser(aId, aUpdateKey, aUrl, aObserver);
}
};

Просмотреть файл

@ -1683,6 +1683,24 @@ function directoryStateDiffers(aState, aCache)
return false;
}
/**
* Wraps a function in an exception handler to protect against exceptions inside callbacks
* @param aFunction function(args...)
* @return function(args...), a function that takes the same arguments as aFunction
* and returns the same result unless aFunction throws, in which case it logs
* a warning and returns undefined.
*/
function makeSafe(aFunction) {
return function(...aArgs) {
try {
return aFunction(...aArgs);
}
catch(ex) {
WARN("XPIProvider callback failed", ex);
}
return undefined;
}
}
var XPIProvider = {
// An array of known install locations
@ -1734,6 +1752,32 @@ var XPIProvider = {
this._telemetryDetails[aId][aName] = aValue;
},
// Keep track of in-progress operations that support cancel()
_inProgress: new Set(),
doing: function XPI_doing(aCancellable) {
this._inProgress.add(aCancellable);
},
done: function XPI_done(aCancellable) {
return this._inProgress.delete(aCancellable);
},
cancelAll: function XPI_cancelAll() {
// Cancelling one may alter _inProgress, so restart the iterator after each
while (this._inProgress.size > 0) {
for (let c of this._inProgress) {
try {
c.cancel();
}
catch (e) {
WARN("Cancel failed", e);
}
this._inProgress.delete(c);
}
}
},
/**
* Adds or updates a URI mapping for an Addon.id.
*
@ -2076,6 +2120,9 @@ var XPIProvider = {
shutdown: function XPI_shutdown() {
LOG("shutdown");
// Stop anything we were doing asynchronously
this.cancelAll();
this.bootstrappedAddons = {};
this.bootstrapScopes = {};
this.enabledAddons = null;
@ -3308,7 +3355,7 @@ var XPIProvider = {
locMigrateData = XPIDatabase.migrateData[installLocation.name];
for (let id in addonStates) {
changed = addMetadata(installLocation, id, addonStates[id],
locMigrateData[id] || null) || changed;
(locMigrateData[id] || null)) || changed;
}
}
@ -3642,10 +3689,7 @@ var XPIProvider = {
*/
getAddonByID: function XPI_getAddonByID(aId, aCallback) {
XPIDatabase.getVisibleAddonForID (aId, function getAddonByID_getVisibleAddonForID(aAddon) {
if (aAddon)
aCallback(createWrapper(aAddon));
else
aCallback(null);
aCallback(createWrapper(aAddon));
});
},
@ -3673,10 +3717,7 @@ var XPIProvider = {
*/
getAddonBySyncGUID: function XPI_getAddonBySyncGUID(aGUID, aCallback) {
XPIDatabase.getAddonBySyncGUID(aGUID, function getAddonBySyncGUID_getAddonBySyncGUID(aAddon) {
if (aAddon)
aCallback(createWrapper(aAddon));
else
aCallback(null);
aCallback(createWrapper(aAddon));
});
},
@ -4382,6 +4423,11 @@ var XPIProvider = {
if ("_hasResourceCache" in aAddon)
aAddon._hasResourceCache = new Map();
if (aAddon._updateCheck) {
LOG("Cancel in-progress update check for " + aAddon.id);
aAddon._updateCheck.cancel();
}
// Inactive add-ons don't require a restart to uninstall
let requiresRestart = this.uninstallRequiresRestart(aAddon);
@ -4631,6 +4677,7 @@ AddonInstall.prototype = {
* The callback to pass the initialised AddonInstall to
*/
initLocalInstall: function AI_initLocalInstall(aCallback) {
aCallback = makeSafe(aCallback);
this.file = this.sourceURI.QueryInterface(Ci.nsIFileURL).file;
if (!this.file.exists()) {
@ -4746,7 +4793,7 @@ AddonInstall.prototype = {
AddonManagerPrivate.callInstallListeners("onNewInstall", this.listeners,
this.wrapper);
aCallback(this);
makeSafe(aCallback)(this);
},
/**
@ -4946,7 +4993,7 @@ AddonInstall.prototype = {
if (!addon) {
// No valid add-on was found
aCallback();
makeSafe(aCallback)();
return;
}
@ -4995,7 +5042,7 @@ AddonInstall.prototype = {
}, this);
}
else {
aCallback();
makeSafe(aCallback)();
}
},
@ -5009,6 +5056,7 @@ AddonInstall.prototype = {
* XPI is incorrectly signed
*/
loadManifest: function AI_loadManifest(aCallback) {
aCallback = makeSafe(aCallback);
let self = this;
function addRepositoryData(aAddon) {
// Try to load from the existing cache first
@ -5680,7 +5728,7 @@ AddonInstall.createInstall = function AI_createInstall(aCallback, aFile) {
}
catch(e) {
ERROR("Error creating install", e);
aCallback(null);
makeSafe(aCallback)(null);
}
};
@ -5819,6 +5867,8 @@ function UpdateChecker(aAddon, aListener, aReason, aAppVersion, aPlatformVersion
Components.utils.import("resource://gre/modules/AddonUpdateChecker.jsm");
this.addon = aAddon;
aAddon._updateCheck = this;
XPIProvider.doing(this);
this.listener = aListener;
this.appVersion = aAppVersion;
this.platformVersion = aPlatformVersion;
@ -5842,8 +5892,8 @@ function UpdateChecker(aAddon, aListener, aReason, aAppVersion, aPlatformVersion
aReason |= UPDATE_TYPE_NEWVERSION;
let url = escapeAddonURI(aAddon, updateURL, aReason, aAppVersion);
AddonUpdateChecker.checkForUpdates(aAddon.id, aAddon.updateKey,
url, this);
this._parser = AddonUpdateChecker.checkForUpdates(aAddon.id, aAddon.updateKey,
url, this);
}
UpdateChecker.prototype = {
@ -5868,7 +5918,7 @@ UpdateChecker.prototype = {
this.listener[aMethod].apply(this.listener, aArgs);
}
catch (e) {
LOG("Exception calling UpdateListener method " + aMethod + ": " + e);
WARN("Exception calling UpdateListener method " + aMethod, e);
}
},
@ -5879,6 +5929,8 @@ UpdateChecker.prototype = {
* The list of update details for the add-on
*/
onUpdateCheckComplete: function UC_onUpdateCheckComplete(aUpdates) {
XPIProvider.done(this.addon._updateCheck);
this.addon._updateCheck = null;
let AUC = AddonUpdateChecker;
let ignoreMaxVersion = false;
@ -5979,9 +6031,23 @@ UpdateChecker.prototype = {
* An error status
*/
onUpdateCheckError: function UC_onUpdateCheckError(aError) {
XPIProvider.done(this.addon._updateCheck);
this.addon._updateCheck = null;
this.callListener("onNoCompatibilityUpdateAvailable", createWrapper(this.addon));
this.callListener("onNoUpdateAvailable", createWrapper(this.addon));
this.callListener("onUpdateFinished", createWrapper(this.addon), aError);
},
/**
* Called to cancel an in-progress update check
*/
cancel: function UC_cancel() {
let parser = this._parser;
if (parser) {
this._parser = null;
// This will call back to onUpdateCheckError with a CANCELLED error
parser.cancel();
}
}
};
@ -6635,6 +6701,15 @@ function AddonWrapper(aAddon) {
new UpdateChecker(aAddon, aListener, aReason, aAppVersion, aPlatformVersion);
};
// Returns true if there was an update in progress, false if there was no update to cancel
this.cancelUpdate = function AddonWrapper_cancelUpdate() {
if (aAddon._updateCheck) {
aAddon._updateCheck.cancel();
return true;
}
return false;
};
this.hasResource = function AddonWrapper_hasResource(aPath) {
if (aAddon._hasResourceCache.has(aPath))
return aAddon._hasResourceCache.get(aPath);

Просмотреть файл

@ -146,10 +146,10 @@ function getRepositoryAddon(aAddon, aCallback) {
/**
* Wrap an API-supplied function in an exception handler to make it safe to call
*/
function safeCallback(aCallback) {
function makeSafe(aCallback) {
return function(...aArgs) {
try {
aCallback.apply(null, aArgs);
aCallback(...aArgs);
}
catch(ex) {
WARN("XPI Database callback failed", ex);
@ -1057,12 +1057,12 @@ this.XPIDatabase = {
this.asyncLoadDB().then(
addonDB => {
let addonList = _filterDB(addonDB, aFilter);
asyncMap(addonList, getRepositoryAddon, safeCallback(aCallback));
asyncMap(addonList, getRepositoryAddon, makeSafe(aCallback));
})
.then(null,
error => {
ERROR("getAddonList failed", e);
safeCallback(aCallback)([]);
makeSafe(aCallback)([]);
});
},
@ -1077,12 +1077,12 @@ this.XPIDatabase = {
getAddon: function(aFilter, aCallback) {
return this.asyncLoadDB().then(
addonDB => {
getRepositoryAddon(_findAddon(addonDB, aFilter), safeCallback(aCallback));
getRepositoryAddon(_findAddon(addonDB, aFilter), makeSafe(aCallback));
})
.then(null,
error => {
ERROR("getAddon failed", e);
safeCallback(aCallback)(null);
makeSafe(aCallback)(null);
});
},
@ -1112,7 +1112,7 @@ this.XPIDatabase = {
getAddonInLocation: function XPIDB_getAddonInLocation(aId, aLocation, aCallback) {
this.asyncLoadDB().then(
addonDB => getRepositoryAddon(addonDB.get(aLocation + ":" + aId),
safeCallback(aCallback)));
makeSafe(aCallback)));
},
/**

Просмотреть файл

@ -16,13 +16,29 @@ const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVer
// Forcibly end the test if it runs longer than 15 minutes
const TIMEOUT_MS = 900000;
Components.utils.import("resource://gre/modules/AddonManager.jsm");
Components.utils.import("resource://gre/modules/AddonRepository.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/FileUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/NetUtil.jsm");
// We need some internal bits of AddonManager
let AMscope = Components.utils.import("resource://gre/modules/AddonManager.jsm");
let AddonManager = AMscope.AddonManager;
let AddonManagerInternal = AMscope.AddonManagerInternal;
// Mock out AddonManager's reference to the AsyncShutdown module so we can shut
// down AddonManager from the test
let MockAsyncShutdown = {
hook: null,
profileBeforeChange: {
addBlocker: function(aName, aBlocker) {
do_print("Mock profileBeforeChange blocker for '" + aName + "'");
MockAsyncShutdown.hook = aBlocker;
}
}
};
AMscope.AsyncShutdown = MockAsyncShutdown;
var gInternalManager = null;
var gAppInfo = null;
var gAddonsList;
@ -403,11 +419,16 @@ function shutdownManager() {
let shutdownDone = false;
Services.obs.notifyObservers(null, "quit-application-granted", null);
let scope = Components.utils.import("resource://gre/modules/AddonManager.jsm");
scope.AddonManagerInternal.shutdown()
.then(
() => shutdownDone = true,
err => shutdownDone = true);
MockAsyncShutdown.hook().then(
() => shutdownDone = true,
err => shutdownDone = true);
let thr = Services.tm.mainThread;
// Wait until we observe the shutdown notifications
while (!shutdownDone) {
thr.processNextEvent(true);
}
gInternalManager = null;
@ -417,20 +438,13 @@ function shutdownManager() {
// Clear any crash report annotations
gAppInfo.annotations = {};
let thr = Services.tm.mainThread;
// Wait until we observe the shutdown notifications
while (!shutdownDone) {
thr.processNextEvent(true);
}
// Force the XPIProvider provider to reload to better
// simulate real-world usage.
scope = Components.utils.import("resource://gre/modules/XPIProvider.jsm");
let XPIscope = Components.utils.import("resource://gre/modules/XPIProvider.jsm");
// This would be cleaner if I could get it as the rejection reason from
// the AddonManagerInternal.shutdown() promise
gXPISaveError = scope.XPIProvider._shutdownError;
AddonManagerPrivate.unregisterProvider(scope.XPIProvider);
gXPISaveError = XPIscope.XPIProvider._shutdownError;
AddonManagerPrivate.unregisterProvider(XPIscope.XPIProvider);
Components.utils.unload("resource://gre/modules/XPIProvider.jsm");
}
@ -1090,16 +1104,24 @@ if ("nsIWindowsRegKey" in AM_Ci) {
var MockRegistry = {
LOCAL_MACHINE: {},
CURRENT_USER: {},
CLASSES_ROOT: {},
setValue: function(aRoot, aPath, aName, aValue) {
getRoot: function(aRoot) {
switch (aRoot) {
case AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE:
var rootKey = MockRegistry.LOCAL_MACHINE;
break
return MockRegistry.LOCAL_MACHINE;
case AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER:
rootKey = MockRegistry.CURRENT_USER;
break
return MockRegistry.CURRENT_USER;
case AM_Ci.nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT:
return MockRegistry.CLASSES_ROOT;
default:
do_throw("Unknown root " + aRootKey);
return null;
}
},
setValue: function(aRoot, aPath, aName, aValue) {
let rootKey = MockRegistry.getRoot(aRoot);
if (!(aPath in rootKey)) {
rootKey[aPath] = [];
@ -1141,14 +1163,7 @@ if ("nsIWindowsRegKey" in AM_Ci) {
// --- Overridden nsIWindowsRegKey interface functions ---
open: function(aRootKey, aRelPath, aMode) {
switch (aRootKey) {
case AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE:
var rootKey = MockRegistry.LOCAL_MACHINE;
break
case AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER:
rootKey = MockRegistry.CURRENT_USER;
break
}
let rootKey = MockRegistry.getRoot(aRootKey);
if (!(aRelPath in rootKey))
rootKey[aRelPath] = [];
@ -1398,9 +1413,9 @@ function changeXPIDBVersion(aNewVersion) {
}
/**
* Raw load of a JSON file
* Load a file into a string
*/
function loadJSON(aFile) {
function loadFile(aFile) {
let data = "";
let fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].
createInstance(Components.interfaces.nsIFileInputStream);
@ -1416,6 +1431,14 @@ function loadJSON(aFile) {
} while (read != 0);
}
cstream.close();
return data;
}
/**
* Raw load of a JSON file
*/
function loadJSON(aFile) {
let data = loadFile(aFile);
do_print("Loaded JSON file " + aFile.path);
return(JSON.parse(data));
}

Просмотреть файл

@ -0,0 +1,66 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Test the cancellable doing/done/cancelAll API in XPIProvider
let scope = Components.utils.import("resource://gre/modules/XPIProvider.jsm");
let XPIProvider = scope.XPIProvider;
function run_test() {
// Check that cancelling with nothing in progress doesn't blow up
XPIProvider.cancelAll();
// Check that a basic object gets cancelled
let getsCancelled = {
isCancelled: false,
cancel: function () {
if (this.isCancelled)
do_throw("Already cancelled");
this.isCancelled = true;
}
};
XPIProvider.doing(getsCancelled);
XPIProvider.cancelAll();
do_check_true(getsCancelled.isCancelled);
// Check that if we complete a cancellable, it doesn't get cancelled
let doesntGetCancelled = {
cancel: () => do_throw("This should not have been cancelled")
};
XPIProvider.doing(doesntGetCancelled);
do_check_true(XPIProvider.done(doesntGetCancelled));
XPIProvider.cancelAll();
// A cancellable that adds a cancellable
getsCancelled.isCancelled = false;
let addsAnother = {
isCancelled: false,
cancel: function () {
if (this.isCancelled)
do_throw("Already cancelled");
this.isCancelled = true;
XPIProvider.doing(getsCancelled);
}
}
XPIProvider.doing(addsAnother);
XPIProvider.cancelAll();
do_check_true(addsAnother.isCancelled);
do_check_true(getsCancelled.isCancelled);
// A cancellable that removes another. This assumes that Set() iterates in the
// order that members were added
let removesAnother = {
isCancelled: false,
cancel: function () {
if (this.isCancelled)
do_throw("Already cancelled");
this.isCancelled = true;
XPIProvider.done(doesntGetCancelled);
}
}
XPIProvider.doing(removesAnother);
XPIProvider.doing(doesntGetCancelled);
XPIProvider.cancelAll();
do_check_true(removesAnother.isCancelled);
}

Просмотреть файл

@ -0,0 +1,149 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Test cancelling add-on update checks while in progress (bug 925389)
Components.utils.import("resource://gre/modules/Promise.jsm");
// The test extension uses an insecure update url.
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
// Set up an HTTP server to respond to update requests
Components.utils.import("resource://testing-common/httpd.js");
const profileDir = gProfD.clone();
profileDir.append("extensions");
// Return a promise that resolves with an addon retrieved by
// AddonManager.getAddonByID()
function promiseGetAddon(aID) {
let p = Promise.defer();
AddonManager.getAddonByID(aID, p.resolve);
return p.promise;
}
function run_test() {
// Kick off the task-based tests...
run_next_test();
}
// Install one extension
// Start download of update check (but delay HTTP response)
// Cancel update check
// - ensure we get cancel notification
// complete HTTP response
// - ensure no callbacks after cancel
// - ensure update is gone
// Create an addon update listener containing a promise
// that resolves when the update is cancelled
function makeCancelListener() {
let updated = Promise.defer();
return {
onUpdateAvailable: function(addon, install) {
updated.reject("Should not have seen onUpdateAvailable notification");
},
onUpdateFinished: function(aAddon, aError) {
do_print("onUpdateCheckFinished: " + aAddon.id + " " + aError);
updated.resolve(aError);
},
promise: updated.promise
};
}
// Set up the HTTP server so that we can control when it responds
let httpReceived = Promise.defer();
function dataHandler(aRequest, aResponse) {
asyncResponse = aResponse;
aResponse.processAsync();
httpReceived.resolve([aRequest, aResponse]);
}
var testserver = new HttpServer();
testserver.registerDirectory("/addons/", do_get_file("addons"));
testserver.registerPathHandler("/data/test_update.rdf", dataHandler);
testserver.start(-1);
gPort = testserver.identity.primaryPort;
// Set up an add-on for update check
writeInstallRDFForExtension({
id: "addon1@tests.mozilla.org",
version: "1.0",
updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
targetApplications: [{
id: "xpcshell@tests.mozilla.org",
minVersion: "1",
maxVersion: "1"
}],
name: "Test Addon 1",
}, profileDir);
add_task(function cancel_during_check() {
startupManager();
let a1 = yield promiseGetAddon("addon1@tests.mozilla.org");
do_check_neq(a1, null);
let listener = makeCancelListener();
a1.findUpdates(listener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
// Wait for the http request to arrive
let [request, response] = yield httpReceived.promise;
// cancelUpdate returns true if there is an update check in progress
do_check_true(a1.cancelUpdate());
let updateResult = yield listener.promise;
do_check_eq(AddonManager.UPDATE_STATUS_CANCELLED, updateResult);
// Now complete the HTTP request
let file = do_get_cwd();
file.append("data");
file.append("test_update.rdf");
let data = loadFile(file);
response.write(data);
response.finish();
// trying to cancel again should return false, i.e. nothing to cancel
do_check_false(a1.cancelUpdate());
yield true;
});
// Test that update check is cancelled if the XPI provider shuts down while
// the update check is in progress
add_task(function shutdown_during_check() {
// Reset our HTTP listener
httpReceived = Promise.defer();
let a1 = yield promiseGetAddon("addon1@tests.mozilla.org");
do_check_neq(a1, null);
let listener = makeCancelListener();
a1.findUpdates(listener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
// Wait for the http request to arrive
let [request, response] = yield httpReceived.promise;
shutdownManager();
let updateResult = yield listener.promise;
do_check_eq(AddonManager.UPDATE_STATUS_CANCELLED, updateResult);
// Now complete the HTTP request
let file = do_get_cwd();
file.append("data");
file.append("test_update.rdf");
let data = loadFile(file);
response.write(data);
response.finish();
// trying to cancel again should return false, i.e. nothing to cancel
do_check_false(a1.cancelUpdate());
yield testserver.stop(Promise.defer().resolve);
});

Просмотреть файл

@ -11,6 +11,7 @@ run-sequentially = Uses hardcoded ports in xpi files.
skip-if = os == "android"
[test_DeferredSave.js]
[test_LightweightThemeManager.js]
[test_XPIcancel.js]
[test_backgroundupdate.js]
[test_bad_json.js]
[test_badschema.js]
@ -230,6 +231,7 @@ fail-if = os == "android"
[test_update.js]
# Bug 676992: test consistently hangs on Android
skip-if = os == "android"
[test_updateCancel.js]
[test_update_strictcompat.js]
# Bug 676992: test consistently hangs on Android
skip-if = os == "android"

Просмотреть файл

@ -317,18 +317,44 @@ APZController::PostDelayedTask(Task* aTask, int aDelayMs)
MessageLoop::current()->PostDelayedTask(FROM_HERE, aTask, aDelayMs);
}
// async scroll notifications
// apzc notifications
class TransformedStartEvent : public nsRunnable
{
NS_IMETHOD Run() {
MetroUtils::FireObserver("apzc-transform-start", L"");
return NS_OK;
}
};
class TransformedEndEvent : public nsRunnable
{
NS_IMETHOD Run() {
MetroUtils::FireObserver("apzc-transform-end", L"");
return NS_OK;
}
};
void
APZController::HandlePanBegin()
APZController::NotifyTransformBegin()
{
MetroUtils::FireObserver("apzc-handle-pan-begin", L"");
if (NS_IsMainThread()) {
MetroUtils::FireObserver("apzc-transform-begin", L"");
return;
}
nsCOMPtr<nsIRunnable> runnable = new TransformedStartEvent();
NS_DispatchToMainThread(runnable);
}
void
APZController::HandlePanEnd()
APZController::NotifyTransformEnd()
{
MetroUtils::FireObserver("apzc-handle-pan-end", L"");
if (NS_IsMainThread()) {
MetroUtils::FireObserver("apzc-transform-end", L"");
return;
}
nsCOMPtr<nsIRunnable> runnable = new TransformedEndEvent();
NS_DispatchToMainThread(runnable);
}
} } }

Просмотреть файл

@ -37,8 +37,8 @@ public:
virtual void HandleLongTap(const mozilla::CSSIntPoint& aPoint, int32_t aModifiers);
virtual void SendAsyncScrollDOMEvent(bool aIsRoot, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize);
virtual void PostDelayedTask(Task* aTask, int aDelayMs);
virtual void HandlePanBegin();
virtual void HandlePanEnd();
virtual void NotifyTransformBegin();
virtual void NotifyTransformEnd();
void SetWidgetListener(nsIWidgetListener* aWidgetListener);
void UpdateScrollOffset(const mozilla::layers::ScrollableLayerGuid& aScrollLayerId, CSSIntPoint& aScrollOffset);

Просмотреть файл

@ -43,7 +43,6 @@ extern UINT sAppShellGeckoMsgId;
static ComPtr<ICoreWindowStatic> sCoreStatic;
static bool sIsDispatching = false;
static bool sWillEmptyThreadQueue = false;
static bool sEmptyingThreadQueue = false;
MetroAppShell::~MetroAppShell()
{
@ -242,12 +241,9 @@ MetroAppShell::DispatchAllGeckoEvents()
return;
}
NS_ASSERTION(NS_IsMainThread(), "DispatchAllXPCOMEvents should be called on the main thread");
NS_ASSERTION(NS_IsMainThread(), "DispatchAllGeckoEvents should be called on the main thread");
sWillEmptyThreadQueue = false;
AutoRestore<bool> dispatching(sEmptyingThreadQueue);
sEmptyingThreadQueue = true;
nsIThread *thread = NS_GetCurrentThread();
NS_ProcessPendingEvents(thread, 0);
}
@ -297,14 +293,6 @@ MetroAppShell::ProcessOneNativeEventIfPresent()
bool
MetroAppShell::ProcessNextNativeEvent(bool mayWait)
{
// NS_ProcessPendingEvents will process thread events *and* call
// nsBaseAppShell::OnProcessNextEvent to process native events. However
// we do not want native events getting dispatched while we are in
// DispatchAllGeckoEvents.
if (sEmptyingThreadQueue) {
return false;
}
if (ProcessOneNativeEventIfPresent()) {
return true;
}

Просмотреть файл

@ -496,6 +496,9 @@ MetroInput::OnPointerPressed(UI::Core::ICoreWindow* aSender,
mRecognizerWantsEvents = true;
mCancelable = true;
mCanceledIds.Clear();
} else {
// Only the first touchstart can be canceled.
mCancelable = false;
}
InitTouchEventTouchList(touchEvent);
@ -1141,7 +1144,7 @@ MetroInput::DeliverNextQueuedTouchEvent()
// Test for chrome vs. content target. To do this we only use the first touch
// point since that will be the input batch target. Cache this for touch events
// since HitTestChrome has to send a dom event.
if (mCancelable && event->message == NS_TOUCH_START && mTouches.Count() == 1) {
if (mCancelable && event->message == NS_TOUCH_START) {
nsRefPtr<Touch> touch = event->touches[0];
LayoutDeviceIntPoint pt = LayoutDeviceIntPoint::FromUntyped(touch->mRefPoint);
bool apzIntersect = mWidget->ApzHitTest(mozilla::ScreenIntPoint(pt.x, pt.y));
@ -1150,7 +1153,7 @@ MetroInput::DeliverNextQueuedTouchEvent()
// If this event is destined for chrome, deliver it directly there bypassing
// the apz.
if (mChromeHitTestCacheForTouch) {
if (!mCancelable && mChromeHitTestCacheForTouch) {
DUMP_TOUCH_IDS("DOM(1)", event);
mWidget->DispatchEvent(event, status);
return;
@ -1164,7 +1167,7 @@ MetroInput::DeliverNextQueuedTouchEvent()
DUMP_TOUCH_IDS("APZC(1)", event);
mWidget->ApzReceiveInputEvent(event, &mTargetAPZCGuid, &transformedEvent);
DUMP_TOUCH_IDS("DOM(2)", event);
mWidget->DispatchEvent(&transformedEvent, status);
mWidget->DispatchEvent(mChromeHitTestCacheForTouch ? event : &transformedEvent, status);
if (event->message == NS_TOUCH_START) {
mContentConsumingTouch = (nsEventStatus_eConsumeNoDefault == status);
// If we know content wants touch here, we can bail early on mCancelable
@ -1207,7 +1210,9 @@ MetroInput::DeliverNextQueuedTouchEvent()
if (mContentConsumingTouch) {
// Only translate if we're dealing with web content that's transformed
// by the apzc.
TransformTouchEvent(event);
if (!mChromeHitTestCacheForTouch) {
TransformTouchEvent(event);
}
DUMP_TOUCH_IDS("DOM(3)", event);
mWidget->DispatchEvent(event, status);
return;
@ -1223,7 +1228,9 @@ MetroInput::DeliverNextQueuedTouchEvent()
DispatchTouchCancel(event);
return;
}
TransformTouchEvent(event);
if (!mChromeHitTestCacheForTouch) {
TransformTouchEvent(event);
}
DUMP_TOUCH_IDS("DOM(4)", event);
mWidget->DispatchEvent(event, status);
}

Просмотреть файл

@ -1631,7 +1631,5 @@ MetroWidget::Observe(nsISupports *subject, const char *topic, const PRUnichar *d
ScrollableLayerGuid guid = ScrollableLayerGuid(mRootLayerTreeId, presShellId, viewId);
APZController::sAPZC->UpdateZoomConstraints(guid, false, CSSToScreenScale(1.0f), CSSToScreenScale(1.0f));
}
else {
return NS_OK;
}
return NS_OK;
}