зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound
This commit is contained in:
Коммит
972b1b5614
|
@ -66,7 +66,7 @@ PaymentUI.prototype = {
|
|||
// Once the user confirm the payment request and makes his choice, we get
|
||||
// back to the DOM part to get the appropriate payment flow information
|
||||
// based on the selected payment provider.
|
||||
content.addEventListener("mozContentEvent", function handleSelection(evt) {
|
||||
this._handleSelection = (function _handleSelection(evt) {
|
||||
let msg = evt.detail;
|
||||
if (msg.id != id) {
|
||||
return;
|
||||
|
@ -78,8 +78,10 @@ PaymentUI.prototype = {
|
|||
_error(msg.errorMsg);
|
||||
}
|
||||
|
||||
content.removeEventListener("mozContentEvent", handleSelection);
|
||||
});
|
||||
content.removeEventListener("mozContentEvent", this._handleSelection);
|
||||
this._handleSelection = null;
|
||||
}).bind(this);
|
||||
content.addEventListener("mozContentEvent", this._handleSelection);
|
||||
|
||||
browser.shell.sendChromeEvent(detail);
|
||||
},
|
||||
|
@ -114,21 +116,31 @@ PaymentUI.prototype = {
|
|||
// At some point the UI would send the created iframe back so the
|
||||
// callbacks for firing DOMRequest events can be loaded on its
|
||||
// content.
|
||||
content.addEventListener("mozContentEvent", (function loadPaymentShim(evt) {
|
||||
if (evt.detail.id != id) {
|
||||
content.removeEventListener("mozContentEvent", loadPaymentShim);
|
||||
this._loadPaymentShim = (function _loadPaymentShim(evt) {
|
||||
let msg = evt.detail;
|
||||
if (msg.id != id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.errorMsg) {
|
||||
content.removeEventListener("mozContentEvent", this._loadPaymentShim);
|
||||
this._loadPaymentShim = null;
|
||||
_error("ERROR_LOADING_PAYMENT_SHIM: " + msg.errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!msg.frame) {
|
||||
content.removeEventListener("mozContentEvent", this._loadPaymentShim);
|
||||
this._loadPaymentShim = null;
|
||||
_error("ERROR_LOADING_PAYMENT_SHIM");
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to load the payment shim file containing the payment callbacks
|
||||
// in the content script.
|
||||
if (!evt.detail.frame && !evt.detail.errorMsg) {
|
||||
_error("ERROR_LOADING_PAYMENT_SHIM");
|
||||
return;
|
||||
}
|
||||
let frame = evt.detail.frame;
|
||||
let frame = msg.frame;
|
||||
let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner)
|
||||
.frameLoader;
|
||||
.frameLoader;
|
||||
let mm = frameLoader.messageManager;
|
||||
try {
|
||||
mm.loadFrameScript(kPaymentShimFile, true);
|
||||
|
@ -140,25 +152,33 @@ PaymentUI.prototype = {
|
|||
}
|
||||
_error("ERROR_LOADING_PAYMENT_SHIM");
|
||||
} finally {
|
||||
content.removeEventListener("mozContentEvent", loadPaymentShim);
|
||||
content.removeEventListener("mozContentEvent", this._loadPaymentShim);
|
||||
this._loadPaymentShim = null;
|
||||
}
|
||||
}).bind(this));
|
||||
}).bind(this);
|
||||
content.addEventListener("mozContentEvent", this._loadPaymentShim);
|
||||
|
||||
// We also listen for UI notifications about a closed payment flow. The UI
|
||||
// should provide the reason of the closure within the 'errorMsg' parameter
|
||||
this._notifyPayFlowClosed = function _notifyPayFlowClosed (evt) {
|
||||
if (evt.detail.id != id) {
|
||||
this._notifyPayFlowClosed = (function _notifyPayFlowClosed(evt) {
|
||||
let msg = evt.detail;
|
||||
if (msg.id != id) {
|
||||
return;
|
||||
}
|
||||
if (evt.detail.errorMsg) {
|
||||
_error(evt.detail.errorMsg);
|
||||
content.removeEventListener("mozContentEvent",
|
||||
this._notifyPayFlowClosed);
|
||||
|
||||
if (msg.type != 'cancel') {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if (msg.errorMsg) {
|
||||
_error(msg.errorMsg);
|
||||
}
|
||||
content.removeEventListener("mozContentEvent",
|
||||
this._notifyPayFlowClosed);
|
||||
this._notifyPayFlowClosed = null;
|
||||
}).bind(this);
|
||||
content.addEventListener("mozContentEvent",
|
||||
this._notifyPayFlowClosed.bind(this));
|
||||
this._notifyPayFlowClosed);
|
||||
|
||||
browser.shell.sendChromeEvent(detail);
|
||||
},
|
||||
|
@ -169,7 +189,21 @@ PaymentUI.prototype = {
|
|||
if (!content) {
|
||||
return;
|
||||
}
|
||||
content.removeEventListener("mozContentEvent", this._notifyPayFlowClosed);
|
||||
|
||||
if (this._handleSelection) {
|
||||
content.removeEventListener("mozContentEvent", this._handleSelection);
|
||||
this._handleSelection = null;
|
||||
}
|
||||
|
||||
if (this._notifyPayFlowClosed) {
|
||||
content.removeEventListener("mozContentEvent", this._notifyPayFlowClosed);
|
||||
this._notifyPayFlowClosed = null;
|
||||
}
|
||||
|
||||
if (this._loadPaymentShim) {
|
||||
content.removeEventListener("mozContentEvent", this._loadPaymentShim);
|
||||
this._loadPaymentShim = null;
|
||||
}
|
||||
},
|
||||
|
||||
getRandomId: function getRandomId() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"revision": "fefcf4aab784c6e9a0c8b3f4b282c21fb0e0ac6e",
|
||||
"revision": "70f416f866858cb2068bffa31118fc4b15b482c9",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -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@;
|
||||
|
|
|
@ -712,6 +712,7 @@ GK_ATOM(onerror, "onerror")
|
|||
GK_ATOM(onfailed, "onfailed")
|
||||
GK_ATOM(onfocus, "onfocus")
|
||||
GK_ATOM(onfrequencychange, "onfrequencychange")
|
||||
GK_ATOM(onspeakerforcedchange, "onspeakerforcedchange")
|
||||
GK_ATOM(onget, "onget")
|
||||
GK_ATOM(ongroupchange, "ongroupchange")
|
||||
GK_ATOM(onhashchange, "onhashchange")
|
||||
|
|
|
@ -654,6 +654,11 @@ NON_IDL_EVENT(warning,
|
|||
EventNameType_None,
|
||||
NS_EVENT)
|
||||
|
||||
NON_IDL_EVENT(speakerforcedchange,
|
||||
NS_SPEAKERMANAGER_SPEAKERFORCEDCHANGE,
|
||||
EventNameType_None,
|
||||
NS_EVENT)
|
||||
|
||||
// Events that only have on* attributes on XUL elements
|
||||
NON_IDL_EVENT(text,
|
||||
NS_TEXT_TEXT,
|
||||
|
|
|
@ -313,6 +313,11 @@ this.PermissionsTable = { geolocation: {
|
|||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
"speaker-control": {
|
||||
app: DENY_ACTION,
|
||||
privileged: ALLOW_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "nsJSUtils.h"
|
||||
#include "nsCxPusher.h"
|
||||
#include "nsIAudioManager.h"
|
||||
#include "SpeakerManagerService.h"
|
||||
#define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1"
|
||||
#endif
|
||||
|
||||
|
@ -170,6 +171,12 @@ AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
|
|||
UnregisterType(data->mType, data->mElementHidden,
|
||||
CONTENT_PROCESS_ID_MAIN, data->mWithVideo);
|
||||
}
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
bool active = AnyAudioChannelIsActive();
|
||||
for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) {
|
||||
mSpeakerManager[i]->SetAudioChannelActive(active);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -559,6 +566,19 @@ AudioChannelService::Notify(nsITimer* aTimer)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
AudioChannelService::AnyAudioChannelIsActive()
|
||||
{
|
||||
for (int i = AUDIO_CHANNEL_INT_LAST - 1;
|
||||
i >= AUDIO_CHANNEL_INT_NORMAL; --i) {
|
||||
if (!mChannelCounters[i].IsEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
AudioChannelService::ChannelsActiveWithHigherPriorityThan(
|
||||
AudioChannelInternalType aType)
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
class SpeakerManagerService;
|
||||
#endif
|
||||
class AudioChannelService
|
||||
: public nsIObserver
|
||||
, public nsITimerCallback
|
||||
|
@ -81,6 +83,21 @@ public:
|
|||
virtual void SetDefaultVolumeControlChannel(AudioChannelType aType,
|
||||
bool aHidden);
|
||||
|
||||
bool AnyAudioChannelIsActive();
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
void RegisterSpeakerManager(SpeakerManagerService* aSpeakerManager)
|
||||
{
|
||||
if (!mSpeakerManager.Contains(aSpeakerManager)) {
|
||||
mSpeakerManager.AppendElement(aSpeakerManager);
|
||||
}
|
||||
}
|
||||
|
||||
void UnregisterSpeakerManager(SpeakerManagerService* aSpeakerManager)
|
||||
{
|
||||
mSpeakerManager.RemoveElement(aSpeakerManager);
|
||||
}
|
||||
#endif
|
||||
protected:
|
||||
void Notify();
|
||||
|
||||
|
@ -163,7 +180,9 @@ protected:
|
|||
AudioChannelAgentData* aData, void *aUnused);
|
||||
|
||||
nsClassHashtable< nsPtrHashKey<AudioChannelAgent>, AudioChannelAgentData > mAgents;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsTArray<SpeakerManagerService*> mSpeakerManager;
|
||||
#endif
|
||||
nsTArray<uint64_t> mChannelCounters[AUDIO_CHANNEL_INT_LAST];
|
||||
|
||||
AudioChannelType mCurrentHigherChannel;
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
#include "nsIObserverService.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "SpeakerManagerService.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::hal;
|
||||
|
@ -120,6 +124,12 @@ AudioChannelServiceChild::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
|
|||
if (obs) {
|
||||
obs->NotifyObservers(nullptr, "audio-channel-agent-changed", nullptr);
|
||||
}
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
bool active = AnyAudioChannelIsActive();
|
||||
for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) {
|
||||
mSpeakerManager[i]->SetAudioChannelActive(active);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -802,6 +802,11 @@ DOMInterfaces = {
|
|||
'nativeType': 'nsDOMAttributeMap',
|
||||
},
|
||||
|
||||
'MozSpeakerManager': {
|
||||
'nativeType': 'mozilla::dom::SpeakerManager',
|
||||
'headerFile': 'SpeakerManager.h'
|
||||
},
|
||||
|
||||
'MozPowerManager': {
|
||||
'nativeType': 'mozilla::dom::PowerManager',
|
||||
},
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
#if defined(MOZ_WIDGET_GONK)
|
||||
#include "nsVolume.h"
|
||||
#include "nsVolumeService.h"
|
||||
#include "SpeakerManagerService.h"
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
@ -586,6 +587,20 @@ ContentChild::RecvSetProcessPrivileges(const ChildPrivileges& aPrivs)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvSpeakerManagerNotify()
|
||||
{
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsRefPtr<SpeakerManagerService> service =
|
||||
SpeakerManagerService::GetSpeakerManagerService();
|
||||
if (service) {
|
||||
service->Notify();
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
static CancelableTask* sFirstIdleTask;
|
||||
|
||||
static void FirstIdle(void)
|
||||
|
|
|
@ -178,6 +178,8 @@ public:
|
|||
|
||||
virtual bool RecvSetOffline(const bool& offline);
|
||||
|
||||
virtual bool RecvSpeakerManagerNotify();
|
||||
|
||||
virtual bool RecvNotifyVisited(const URIParams& aURI);
|
||||
// auto remove when alertfinished is received.
|
||||
nsresult AddRemoteAlertObserver(const nsString& aData, nsIObserver* aObserver);
|
||||
|
|
|
@ -113,6 +113,7 @@
|
|||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "nsIVolume.h"
|
||||
#include "nsIVolumeService.h"
|
||||
#include "SpeakerManagerService.h"
|
||||
using namespace mozilla::system;
|
||||
#endif
|
||||
|
||||
|
@ -2478,6 +2479,35 @@ ContentParent::RecvPSpeechSynthesisConstructor(PSpeechSynthesisParent* aActor)
|
|||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvSpeakerManagerGetSpeakerStatus(bool* aValue)
|
||||
{
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
*aValue = false;
|
||||
nsRefPtr<SpeakerManagerService> service =
|
||||
SpeakerManagerService::GetSpeakerManagerService();
|
||||
if (service) {
|
||||
*aValue = service->GetSpeakerStatus();
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvSpeakerManagerForceSpeaker(const bool& aEnable)
|
||||
{
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsRefPtr<SpeakerManagerService> service =
|
||||
SpeakerManagerService::GetSpeakerManagerService();
|
||||
if (service) {
|
||||
service->ForceSpeaker(aEnable, mChildID);
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvStartVisitedQuery(const URIParams& aURI)
|
||||
{
|
||||
|
@ -2800,6 +2830,8 @@ AddGeolocationListener(nsIDOMGeoPositionCallback* watcher, bool highAccuracy)
|
|||
}
|
||||
|
||||
PositionOptions* options = new PositionOptions();
|
||||
options->mTimeout = 0;
|
||||
options->mMaximumAge = 0;
|
||||
options->mEnableHighAccuracy = highAccuracy;
|
||||
int32_t retval = 1;
|
||||
geo->WatchPosition(watcher, nullptr, options, &retval);
|
||||
|
|
|
@ -474,6 +474,10 @@ private:
|
|||
|
||||
virtual bool RecvBroadcastVolume(const nsString& aVolumeName);
|
||||
|
||||
virtual bool RecvSpeakerManagerGetSpeakerStatus(bool* aValue);
|
||||
|
||||
virtual bool RecvSpeakerManagerForceSpeaker(const bool& aEnable);
|
||||
|
||||
virtual bool RecvSystemMessageHandled() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvNuwaReady() MOZ_OVERRIDE;
|
||||
|
|
|
@ -238,6 +238,8 @@ child:
|
|||
*/
|
||||
async AudioChannelNotify();
|
||||
|
||||
async SpeakerManagerNotify();
|
||||
|
||||
/**
|
||||
* Do a memory info dump to a file in our temp directory.
|
||||
*
|
||||
|
@ -247,7 +249,6 @@ child:
|
|||
async DumpMemoryInfoToTempDir(nsString identifier,
|
||||
bool minimizeMemoryUsage,
|
||||
bool dumpChildProcesses);
|
||||
|
||||
/**
|
||||
* Dump this process's GC and CC logs.
|
||||
*
|
||||
|
@ -471,6 +472,11 @@ parent:
|
|||
sync KeywordToURI(nsCString keyword)
|
||||
returns (OptionalInputStreamParams postData, OptionalURIParams uri);
|
||||
|
||||
sync SpeakerManagerForceSpeaker(bool aEnable);
|
||||
|
||||
sync SpeakerManagerGetSpeakerStatus()
|
||||
returns (bool value);
|
||||
|
||||
both:
|
||||
AsyncMessage(nsString aMessage, ClonedMessageData aData,
|
||||
CpowEntry[] aCpows, Principal aPrincipal);
|
||||
|
|
|
@ -1781,7 +1781,10 @@ MobileMessageDatabaseService.prototype = {
|
|||
forEachMatchedMmsDeliveryInfo:
|
||||
function forEachMatchedMmsDeliveryInfo(aDeliveryInfo, aNeedle, aCallback) {
|
||||
|
||||
let typedAddress = MMS.Address.resolveType(aNeedle);
|
||||
let typedAddress = {
|
||||
type: MMS.Address.resolveType(aNeedle),
|
||||
address: aNeedle
|
||||
};
|
||||
let normalizedAddress, parsedAddress;
|
||||
if (typedAddress.type === "PLMN") {
|
||||
normalizedAddress = PhoneNumberUtils.normalize(aNeedle, false);
|
||||
|
@ -1789,7 +1792,10 @@ MobileMessageDatabaseService.prototype = {
|
|||
}
|
||||
|
||||
for (let element of aDeliveryInfo) {
|
||||
let typedStoredAddress = MMS.Address.resolveType(element.receiver);
|
||||
let typedStoredAddress = {
|
||||
type: MMS.Address.resolveType(element.receiver),
|
||||
address: element.receiver
|
||||
};
|
||||
if (typedAddress.type !== typedStoredAddress.type) {
|
||||
// Not even my type. Skip.
|
||||
continue;
|
||||
|
|
|
@ -81,7 +81,10 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
|||
PARALLEL_DIRS += ['plugins/ipc/hangui']
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
PARALLEL_DIRS += ['wifi']
|
||||
PARALLEL_DIRS += [
|
||||
'speakermanager',
|
||||
'wifi',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_B2G_RIL']:
|
||||
PARALLEL_DIRS += [
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "SpeakerManager.h"
|
||||
#include "nsIDOMClassInfo.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "SpeakerManagerService.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsDOMEvent.h"
|
||||
#include "AudioChannelService.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
NS_IMPL_QUERY_INTERFACE_INHERITED1(SpeakerManager, nsDOMEventTargetHelper,
|
||||
nsIDOMEventListener)
|
||||
NS_IMPL_ADDREF_INHERITED(SpeakerManager, nsDOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(SpeakerManager, nsDOMEventTargetHelper)
|
||||
|
||||
SpeakerManager::SpeakerManager()
|
||||
: mForcespeaker(false)
|
||||
, mVisible(false)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
SpeakerManagerService *service =
|
||||
SpeakerManagerService::GetSpeakerManagerService();
|
||||
if (service) {
|
||||
service->RegisterSpeakerManager(this);
|
||||
}
|
||||
}
|
||||
|
||||
SpeakerManager::~SpeakerManager()
|
||||
{
|
||||
SpeakerManagerService *service = SpeakerManagerService::GetSpeakerManagerService();
|
||||
if (service) {
|
||||
service->UnRegisterSpeakerManager(this);
|
||||
}
|
||||
nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner());
|
||||
NS_ENSURE_TRUE_VOID(target);
|
||||
|
||||
target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
||||
this,
|
||||
/* useCapture = */ true);
|
||||
}
|
||||
|
||||
bool
|
||||
SpeakerManager::Speakerforced()
|
||||
{
|
||||
// If a background app calls forcespeaker=true that doesn't change anything.
|
||||
// 'speakerforced' remains false everywhere.
|
||||
if (mForcespeaker && !mVisible) {
|
||||
return false;
|
||||
}
|
||||
SpeakerManagerService *service = SpeakerManagerService::GetSpeakerManagerService();
|
||||
if (service) {
|
||||
return service->GetSpeakerStatus();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
SpeakerManager::Forcespeaker()
|
||||
{
|
||||
return mForcespeaker;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManager::SetForcespeaker(bool aEnable)
|
||||
{
|
||||
SpeakerManagerService *service = SpeakerManagerService::GetSpeakerManagerService();
|
||||
if (service) {
|
||||
service->ForceSpeaker(aEnable, mVisible);
|
||||
}
|
||||
mForcespeaker = aEnable;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManager::DispatchSimpleEvent(const nsAString& aStr)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
|
||||
nsresult rv = CheckInnerWindowCorrectness();
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to create the error event!!!");
|
||||
return;
|
||||
}
|
||||
rv = event->InitEvent(aStr, false, false);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to init the error event!!!");
|
||||
return;
|
||||
}
|
||||
|
||||
event->SetTrusted(true);
|
||||
|
||||
rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("Failed to dispatch the event!!!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManager::Init(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
BindToOwner(aWindow->IsOuterWindow() ?
|
||||
aWindow->GetCurrentInnerWindow() : aWindow);
|
||||
|
||||
mVisible = !GetOwner()->IsBackground();
|
||||
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
|
||||
NS_ENSURE_TRUE_VOID(target);
|
||||
|
||||
target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
||||
this,
|
||||
/* useCapture = */ true,
|
||||
/* wantsUntrusted = */ false);
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
SpeakerManager::GetParentObject() const
|
||||
{
|
||||
return GetOwner();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<SpeakerManager>
|
||||
SpeakerManager::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!sgo) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> ownerWindow = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!ownerWindow) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permMgr =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
|
||||
NS_ENSURE_TRUE(permMgr, nullptr);
|
||||
|
||||
uint32_t permission = nsIPermissionManager::DENY_ACTION;
|
||||
nsresult rv =
|
||||
permMgr->TestPermissionFromWindow(ownerWindow, "speaker-control",
|
||||
&permission);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
if (permission != nsIPermissionManager::ALLOW_ACTION) {
|
||||
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<SpeakerManager> object = new SpeakerManager();
|
||||
object->Init(ownerWindow);
|
||||
return object.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
SpeakerManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
||||
{
|
||||
return MozSpeakerManagerBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SpeakerManager::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
nsAutoString type;
|
||||
aEvent->GetType(type);
|
||||
|
||||
if (!type.EqualsLiteral("visibilitychange")) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
|
||||
NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE);
|
||||
docshell->GetIsActive(&mVisible);
|
||||
|
||||
// If an app that has called forcespeaker=true is switched
|
||||
// from the background to the foreground 'speakerforced'
|
||||
// switches to true in all apps. I.e. the app doesn't have to
|
||||
// call forcespeaker=true again when it comes into foreground.
|
||||
SpeakerManagerService *service =
|
||||
SpeakerManagerService::GetSpeakerManagerService();
|
||||
if (service && mVisible && mForcespeaker) {
|
||||
service->ForceSpeaker(mForcespeaker, mVisible);
|
||||
}
|
||||
// If an application that has called forcespeaker=true, but no audio is
|
||||
// currently playing in the app itself, if application switch to
|
||||
// the background, we switch 'speakerforced' to false.
|
||||
if (!mVisible && mForcespeaker) {
|
||||
AudioChannelService* audioChannelService =
|
||||
AudioChannelService::GetAudioChannelService();
|
||||
if (audioChannelService && !audioChannelService->AnyAudioChannelIsActive()) {
|
||||
service->ForceSpeaker(false, mVisible);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManager::SetAudioChannelActive(bool isActive)
|
||||
{
|
||||
if (!isActive && !mVisible) {
|
||||
SpeakerManagerService *service =
|
||||
SpeakerManagerService::GetSpeakerManagerService();
|
||||
if (service) {
|
||||
service->ForceSpeaker(false, mVisible);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_SpeakerManager_h
|
||||
#define mozilla_dom_SpeakerManager_h
|
||||
|
||||
#include "nsDOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/MozSpeakerManagerBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
/* This class is used for UA to control devices's speaker status.
|
||||
* After UA set the speaker status, the UA should handle the
|
||||
* forcespeakerchange event and change the speaker status in UI.
|
||||
* The device's speaker status would set back to normal when UA close the application.
|
||||
*/
|
||||
class SpeakerManager MOZ_FINAL
|
||||
: public nsDOMEventTargetHelper
|
||||
, public nsIDOMEventListener
|
||||
{
|
||||
friend class SpeakerManagerService;
|
||||
friend class SpeakerManagerServiceChild;
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
public:
|
||||
void Init(nsPIDOMWindow* aWindow);
|
||||
|
||||
nsPIDOMWindow* GetParentObject() const;
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
/**
|
||||
* WebIDL Interface
|
||||
*/
|
||||
// Get this api's force speaker setting.
|
||||
bool Forcespeaker();
|
||||
// Force acoustic sound go through speaker. Don't force to speaker if application
|
||||
// stay in the background and re-force when application
|
||||
// go to foreground
|
||||
void SetForcespeaker(bool aEnable);
|
||||
// Get the device's speaker forced setting.
|
||||
bool Speakerforced();
|
||||
|
||||
void SetAudioChannelActive(bool aIsActive);
|
||||
IMPL_EVENT_HANDLER(speakerforcedchange)
|
||||
|
||||
static already_AddRefed<SpeakerManager>
|
||||
Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
|
||||
|
||||
protected:
|
||||
SpeakerManager();
|
||||
~SpeakerManager();
|
||||
void DispatchSimpleEvent(const nsAString& aStr);
|
||||
// This api's force speaker setting
|
||||
bool mForcespeaker;
|
||||
bool mVisible;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_SpeakerManager_h
|
|
@ -0,0 +1,199 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "SpeakerManagerService.h"
|
||||
#include "SpeakerManagerServiceChild.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "mozilla/Util.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "nsIPropertyBag2.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "AudioChannelService.h"
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1"
|
||||
#include "nsIAudioManager.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
StaticRefPtr<SpeakerManagerService> gSpeakerManagerService;
|
||||
|
||||
// static
|
||||
SpeakerManagerService*
|
||||
SpeakerManagerService::GetSpeakerManagerService()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
return SpeakerManagerServiceChild::GetSpeakerManagerService();
|
||||
}
|
||||
|
||||
// If we already exist, exit early
|
||||
if (gSpeakerManagerService) {
|
||||
return gSpeakerManagerService;
|
||||
}
|
||||
|
||||
// Create new instance, register, return
|
||||
nsRefPtr<SpeakerManagerService> service = new SpeakerManagerService();
|
||||
NS_ENSURE_TRUE(service, nullptr);
|
||||
|
||||
gSpeakerManagerService = service;
|
||||
return gSpeakerManagerService;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerService::Shutdown()
|
||||
{
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
return SpeakerManagerServiceChild::Shutdown();
|
||||
}
|
||||
|
||||
if (gSpeakerManagerService) {
|
||||
gSpeakerManagerService = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(SpeakerManagerService, nsIObserver)
|
||||
|
||||
void
|
||||
SpeakerManagerService::ForceSpeaker(bool aEnable, uint64_t aChildId)
|
||||
{
|
||||
TuruOnSpeaker(aEnable);
|
||||
if (aEnable) {
|
||||
mSpeakerStatusSet.Put(aChildId);
|
||||
}
|
||||
Notify();
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerService::ForceSpeaker(bool aEnable, bool aVisible)
|
||||
{
|
||||
// b2g main process without oop
|
||||
TuruOnSpeaker(aEnable && aVisible);
|
||||
mVisible = aVisible;
|
||||
mOrgSpeakerStatus = aEnable;
|
||||
Notify();
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerService::TuruOnSpeaker(bool aOn)
|
||||
{
|
||||
nsCOMPtr<nsIAudioManager> audioManager = do_GetService(NS_AUDIOMANAGER_CONTRACTID);
|
||||
NS_ENSURE_TRUE_VOID(audioManager);
|
||||
int32_t phoneState;
|
||||
audioManager->GetPhoneState(&phoneState);
|
||||
int32_t forceuse = (phoneState == nsIAudioManager::PHONE_STATE_IN_CALL ||
|
||||
phoneState == nsIAudioManager::PHONE_STATE_IN_COMMUNICATION)
|
||||
? nsIAudioManager::USE_COMMUNICATION : nsIAudioManager::USE_MEDIA;
|
||||
if (aOn) {
|
||||
audioManager->SetForceForUse(forceuse, nsIAudioManager::FORCE_SPEAKER);
|
||||
} else {
|
||||
audioManager->SetForceForUse(forceuse, nsIAudioManager::FORCE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SpeakerManagerService::GetSpeakerStatus()
|
||||
{
|
||||
char propQemu[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.kernel.qemu", propQemu, "");
|
||||
if (!strncmp(propQemu, "1", 1)) {
|
||||
return mOrgSpeakerStatus;
|
||||
}
|
||||
nsCOMPtr<nsIAudioManager> audioManager = do_GetService(NS_AUDIOMANAGER_CONTRACTID);
|
||||
NS_ENSURE_TRUE(audioManager, false);
|
||||
int32_t usage;
|
||||
audioManager->GetForceForUse(nsIAudioManager::USE_MEDIA, &usage);
|
||||
return usage == nsIAudioManager::FORCE_SPEAKER;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerService::Notify()
|
||||
{
|
||||
// Parent Notify to all the child processes.
|
||||
nsTArray<ContentParent*> children;
|
||||
ContentParent::GetAll(children);
|
||||
for (uint32_t i = 0; i < children.Length(); i++) {
|
||||
unused << children[i]->SendSpeakerManagerNotify();
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < mRegisteredSpeakerManagers.Length(); i++) {
|
||||
mRegisteredSpeakerManagers[i]->
|
||||
DispatchSimpleEvent(NS_LITERAL_STRING("speakerforcedchange"));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerService::SetAudioChannelActive(bool aIsActive)
|
||||
{
|
||||
if (!aIsActive && !mVisible) {
|
||||
ForceSpeaker(!mOrgSpeakerStatus, mVisible);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SpeakerManagerService::Observe(nsISupports* aSubject, const char*
|
||||
aTopic, const PRUnichar* aData)
|
||||
{
|
||||
if (!strcmp(aTopic, "ipc:content-shutdown")) {
|
||||
nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
|
||||
if (!props) {
|
||||
NS_WARNING("ipc:content-shutdown message without property bag as subject");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint64_t childID = 0;
|
||||
nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
|
||||
&childID);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// If the audio has paused by audiochannel,
|
||||
// the enable flag should be false and don't need to handle.
|
||||
if (mSpeakerStatusSet.Contains(childID)) {
|
||||
TuruOnSpeaker(false);
|
||||
mSpeakerStatusSet.Remove(childID);
|
||||
}
|
||||
if (mOrgSpeakerStatus) {
|
||||
TuruOnSpeaker(!mOrgSpeakerStatus);
|
||||
mOrgSpeakerStatus = false;
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("ipc:content-shutdown message without childID property");
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
SpeakerManagerService::SpeakerManagerService()
|
||||
: mOrgSpeakerStatus(false),
|
||||
mVisible(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(SpeakerManagerService);
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Default) {
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->AddObserver(this, "ipc:content-shutdown", false);
|
||||
}
|
||||
}
|
||||
AudioChannelService* audioChannelService =
|
||||
AudioChannelService::GetAudioChannelService();
|
||||
if (audioChannelService) {
|
||||
audioChannelService->RegisterSpeakerManager(this);
|
||||
}
|
||||
}
|
||||
|
||||
SpeakerManagerService::~SpeakerManagerService()
|
||||
{
|
||||
MOZ_COUNT_DTOR(SpeakerManagerService);
|
||||
AudioChannelService* audioChannelService =
|
||||
AudioChannelService::GetAudioChannelService();
|
||||
if (audioChannelService)
|
||||
audioChannelService->UnregisterSpeakerManager(this);
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_SpeakerManagerService_h__
|
||||
#define mozilla_dom_SpeakerManagerService_h__
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsTArray.h"
|
||||
#include "SpeakerManager.h"
|
||||
#include "nsIAudioManager.h"
|
||||
#include "nsCheapSets.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class SpeakerManagerService : public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static SpeakerManagerService* GetSpeakerManagerService();
|
||||
virtual void ForceSpeaker(bool aEnable, bool aVisible);
|
||||
virtual bool GetSpeakerStatus();
|
||||
virtual void SetAudioChannelActive(bool aIsActive);
|
||||
// Called by child
|
||||
void ForceSpeaker(bool enable, uint64_t aChildid);
|
||||
// Register the SpeakerManager to service for notify the speakerforcedchange event
|
||||
void RegisterSpeakerManager(SpeakerManager* aSpeakerManager)
|
||||
{
|
||||
mRegisteredSpeakerManagers.AppendElement(aSpeakerManager);
|
||||
}
|
||||
void UnRegisterSpeakerManager(SpeakerManager* aSpeakerManager)
|
||||
{
|
||||
mRegisteredSpeakerManagers.RemoveElement(aSpeakerManager);
|
||||
}
|
||||
/**
|
||||
* Shutdown the singleton.
|
||||
*/
|
||||
static void Shutdown();
|
||||
|
||||
protected:
|
||||
SpeakerManagerService();
|
||||
|
||||
virtual ~SpeakerManagerService();
|
||||
// Notify to UA if device speaker status changed
|
||||
virtual void Notify();
|
||||
|
||||
void TuruOnSpeaker(bool aEnable);
|
||||
|
||||
nsTArray<nsRefPtr<SpeakerManager> > mRegisteredSpeakerManagers;
|
||||
// Set for remember all the child speaker status
|
||||
nsCheapSet<nsUint64HashKey> mSpeakerStatusSet;
|
||||
// The Speaker status assign by UA
|
||||
bool mOrgSpeakerStatus;
|
||||
|
||||
bool mVisible;
|
||||
// This is needed for IPC communication between
|
||||
// SpeakerManagerServiceChild and this class.
|
||||
friend class ContentParent;
|
||||
friend class ContentChild;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -0,0 +1,113 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "SpeakerManagerServiceChild.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "mozilla/Util.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "AudioChannelService.h"
|
||||
#include <cutils/properties.h>
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
StaticRefPtr<SpeakerManagerServiceChild> gSpeakerManagerServiceChild;
|
||||
|
||||
// static
|
||||
SpeakerManagerService*
|
||||
SpeakerManagerServiceChild::GetSpeakerManagerService()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// If we already exist, exit early
|
||||
if (gSpeakerManagerServiceChild) {
|
||||
return gSpeakerManagerServiceChild;
|
||||
}
|
||||
|
||||
// Create new instance, register, return
|
||||
nsRefPtr<SpeakerManagerServiceChild> service = new SpeakerManagerServiceChild();
|
||||
NS_ENSURE_TRUE(service, nullptr);
|
||||
|
||||
gSpeakerManagerServiceChild = service;
|
||||
return gSpeakerManagerServiceChild;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerServiceChild::ForceSpeaker(bool aEnable, bool aVisible)
|
||||
{
|
||||
mVisible = aVisible;
|
||||
mOrgSpeakerStatus = aEnable;
|
||||
ContentChild *cc = ContentChild::GetSingleton();
|
||||
if (cc) {
|
||||
cc->SendSpeakerManagerForceSpeaker(aEnable && aVisible);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SpeakerManagerServiceChild::GetSpeakerStatus()
|
||||
{
|
||||
ContentChild *cc = ContentChild::GetSingleton();
|
||||
bool status = false;
|
||||
if (cc) {
|
||||
cc->SendSpeakerManagerGetSpeakerStatus(&status);
|
||||
}
|
||||
char propQemu[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.kernel.qemu", propQemu, "");
|
||||
if (!strncmp(propQemu, "1", 1)) {
|
||||
return mOrgSpeakerStatus;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerServiceChild::Shutdown()
|
||||
{
|
||||
if (gSpeakerManagerServiceChild) {
|
||||
gSpeakerManagerServiceChild = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerServiceChild::SetAudioChannelActive(bool aIsActive)
|
||||
{
|
||||
// Content process and switch to background with no audio and speaker forced.
|
||||
// Then disable speaker
|
||||
for (uint32_t i = 0; i < mRegisteredSpeakerManagers.Length(); i++) {
|
||||
mRegisteredSpeakerManagers[i]->SetAudioChannelActive(aIsActive);
|
||||
}
|
||||
}
|
||||
|
||||
SpeakerManagerServiceChild::SpeakerManagerServiceChild()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
AudioChannelService* audioChannelService = AudioChannelService::GetAudioChannelService();
|
||||
if (audioChannelService) {
|
||||
audioChannelService->RegisterSpeakerManager(this);
|
||||
}
|
||||
MOZ_COUNT_CTOR(SpeakerManagerServiceChild);
|
||||
}
|
||||
|
||||
SpeakerManagerServiceChild::~SpeakerManagerServiceChild()
|
||||
{
|
||||
AudioChannelService* audioChannelService = AudioChannelService::GetAudioChannelService();
|
||||
if (audioChannelService) {
|
||||
audioChannelService->UnregisterSpeakerManager(this);
|
||||
}
|
||||
MOZ_COUNT_DTOR(SpeakerManagerServiceChild);
|
||||
}
|
||||
|
||||
void
|
||||
SpeakerManagerServiceChild::Notify()
|
||||
{
|
||||
for (uint32_t i = 0; i < mRegisteredSpeakerManagers.Length(); i++) {
|
||||
mRegisteredSpeakerManagers[i]->DispatchSimpleEvent(NS_LITERAL_STRING("speakerforcedchange"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_SpeakerManagerServicechild_h__
|
||||
#define mozilla_dom_SpeakerManagerServicechild_h__
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsISupports.h"
|
||||
#include "SpeakerManagerService.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
/* This class is used to do the IPC to enable/disable speaker status
|
||||
Also handle the application speaker competition problem
|
||||
*/
|
||||
class SpeakerManagerServiceChild : public SpeakerManagerService
|
||||
{
|
||||
public:
|
||||
static SpeakerManagerService* GetSpeakerManagerService();
|
||||
static void Shutdown();
|
||||
virtual void ForceSpeaker(bool aEnable, bool aVisible) MOZ_OVERRIDE;
|
||||
virtual bool GetSpeakerStatus() MOZ_OVERRIDE;
|
||||
virtual void SetAudioChannelActive(bool aIsActive) MOZ_OVERRIDE;
|
||||
virtual void Notify() MOZ_OVERRIDE;
|
||||
protected:
|
||||
SpeakerManagerServiceChild();
|
||||
virtual ~SpeakerManagerServiceChild();
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
|
||||
|
||||
XPIDL_MODULE = 'dom_speakermanager'
|
||||
|
||||
EXPORTS += [
|
||||
'SpeakerManager.h',
|
||||
'SpeakerManagerService.h',
|
||||
'SpeakerManagerServiceChild.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'SpeakerManager.cpp',
|
||||
'SpeakerManagerService.cpp',
|
||||
'SpeakerManagerServiceChild.cpp',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'gklayout'
|
|
@ -0,0 +1,3 @@
|
|||
[DEFAULT]
|
||||
|
||||
[test_speakermanager.html]
|
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test MozSpeakerManager API</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
"use strict";
|
||||
|
||||
function testObject() {
|
||||
var mgr = new MozSpeakerManager();
|
||||
var spkforced = false;
|
||||
mgr.onspeakerforcedchange = function() {
|
||||
if (spkforced) {
|
||||
is(mgr.speakerforced, true, 'speaker should be true');
|
||||
spkforced = false;
|
||||
mgr.forcespeaker = false;
|
||||
} else {
|
||||
is(mgr.speakerforced, false, 'speaker should be false');
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
spkforced = true;
|
||||
mgr.forcespeaker = true;
|
||||
}
|
||||
|
||||
function startTests() {
|
||||
// Currently applicable only on FxOS
|
||||
if (navigator.userAgent.indexOf("Mobile") != -1 &&
|
||||
navigator.appVersion.indexOf("Android") == -1) {
|
||||
testObject();
|
||||
} else {
|
||||
ok(true, "mozAlarms on Firefox OS only.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPermissions(
|
||||
[{ "type": "speaker-control", "allow": 1, "context": document }],
|
||||
startTests);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1613,12 +1613,6 @@ RadioInterface.prototype = {
|
|||
this.clientId, status);
|
||||
},
|
||||
|
||||
_isRadioChanging: function _isRadioChanging() {
|
||||
let state = this.rilContext.detailedRadioState;
|
||||
return state == RIL.GECKO_DETAILED_RADIOSTATE_ENABLING ||
|
||||
state == RIL.GECKO_DETAILED_RADIOSTATE_DISABLING;
|
||||
},
|
||||
|
||||
_convertRadioState: function _converRadioState(state) {
|
||||
switch (state) {
|
||||
case RIL.GECKO_RADIOSTATE_OFF:
|
||||
|
@ -1858,7 +1852,11 @@ RadioInterface.prototype = {
|
|||
if (DEBUG) this.debug("Don't connect data call when Wifi is connected.");
|
||||
return;
|
||||
}
|
||||
if (this._isRadioChanging()) {
|
||||
|
||||
let detailedRadioState = this.rilContext.detailedRadioState;
|
||||
if (gRadioEnabledController.isDeactivatingDataCalls() ||
|
||||
detailedRadioState == RIL.GECKO_DETAILED_RADIOSTATE_ENABLING ||
|
||||
detailedRadioState == RIL.GECKO_DETAILED_RADIOSTATE_DISABLING) {
|
||||
// We're changing the radio power currently, ignore any changes.
|
||||
return;
|
||||
}
|
||||
|
@ -2065,7 +2063,7 @@ RadioInterface.prototype = {
|
|||
// At this point we could send a message to content to notify the user
|
||||
// that storing an incoming SMS failed, most likely due to a full disk.
|
||||
if (DEBUG) {
|
||||
this.debug("Could not store SMS " + message.id + ", error code " + rv);
|
||||
this.debug("Could not store SMS, error code " + rv);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -2075,8 +2073,8 @@ RadioInterface.prototype = {
|
|||
}.bind(this);
|
||||
|
||||
if (message.messageClass != RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0]) {
|
||||
message.id = gMobileMessageDatabaseService.saveReceivedMessage(message,
|
||||
notifyReceived);
|
||||
gMobileMessageDatabaseService.saveReceivedMessage(message,
|
||||
notifyReceived);
|
||||
} else {
|
||||
message.id = -1;
|
||||
message.threadId = 0;
|
||||
|
@ -2638,18 +2636,15 @@ RadioInterface.prototype = {
|
|||
},
|
||||
|
||||
isValidStateForSetRadioEnabled: function() {
|
||||
let state = this.rilContext.radioState;
|
||||
|
||||
return !this._isRadioChanging() &&
|
||||
(state == RIL.GECKO_RADIOSTATE_READY ||
|
||||
state == RIL.GECKO_RADIOSTATE_OFF);
|
||||
let state = this.rilContext.detailedRadioState;
|
||||
return state == RIL.GECKO_DETAILED_RADIOSTATE_ENABLED ||
|
||||
state == RIL.GECKO_DETAILED_RADIOSTATE_DISABLED;
|
||||
},
|
||||
|
||||
isDummyForSetRadioEnabled: function(message) {
|
||||
let state = this.rilContext.radioState;
|
||||
|
||||
return (state == RIL.GECKO_RADIOSTATE_READY && message.enabled) ||
|
||||
(state == RIL.GECKO_RADIOSTATE_OFF && !message.enabled);
|
||||
let state = this.rilContext.detailedRadioState;
|
||||
return (state == RIL.GECKO_DETAILED_RADIOSTATE_ENABLED && message.enabled) ||
|
||||
(state == RIL.GECKO_DETAILED_RADIOSTATE_DISABLED && !message.enabled);
|
||||
},
|
||||
|
||||
setRadioEnabledResponse: function(target, message, errorMsg) {
|
||||
|
|
|
@ -374,6 +374,7 @@ var interfaceNamesInGlobalScope =
|
|||
"MozSmsFilter",
|
||||
"MozSmsMessage",
|
||||
"MozSmsSegmentInfo",
|
||||
{name: "MozSpeakerManager", b2g: true},
|
||||
{name: "MozStkCommandEvent", b2g: true, pref: "dom.icc.enabled"},
|
||||
{name: "MozTimeManager", b2g: true},
|
||||
{name: "MozVoicemail", b2g: true, pref: "dom.voicemail.enabled"},
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Allow application can control acoustic sound output through speaker.
|
||||
* Reference https://wiki.mozilla.org/WebAPI/SpeakerManager
|
||||
*/
|
||||
[Constructor()]
|
||||
interface MozSpeakerManager : EventTarget {
|
||||
/* query the speaker status */
|
||||
readonly attribute boolean speakerforced;
|
||||
/* force device device's acoustic sound output through speaker */
|
||||
attribute boolean forcespeaker;
|
||||
/* this event will be fired when device's speaker forced status change */
|
||||
attribute EventHandler onspeakerforcedchange;
|
||||
};
|
|
@ -532,6 +532,7 @@ if CONFIG['MOZ_NFC']:
|
|||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
WEBIDL_FILES += [
|
||||
'MozSpeakerManager.webidl',
|
||||
'MozWifiConnectionInfoEvent.webidl',
|
||||
'MozWifiStatusChangeEvent.webidl',
|
||||
]
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -36,8 +36,8 @@ static int sRadioFD;
|
|||
static bool sRadioEnabled;
|
||||
static pthread_t sRadioThread;
|
||||
static hal::FMRadioSettings sRadioSettings;
|
||||
static int sTavaruaVersion;
|
||||
static bool sTavaruaMode;
|
||||
static int sMsmFMVersion;
|
||||
static bool sMsmFMMode;
|
||||
|
||||
static int
|
||||
setControl(uint32_t id, int32_t value)
|
||||
|
@ -69,12 +69,12 @@ public:
|
|||
|
||||
/* Runs on the radio thread */
|
||||
static void
|
||||
initTavaruaRadio(hal::FMRadioSettings &aInfo)
|
||||
initMsmFMRadio(hal::FMRadioSettings &aInfo)
|
||||
{
|
||||
mozilla::ScopedClose fd(sRadioFD);
|
||||
char version[64];
|
||||
int rc;
|
||||
snprintf(version, sizeof(version), "%d", sTavaruaVersion);
|
||||
snprintf(version, sizeof(version), "%d", sMsmFMVersion);
|
||||
property_set("hw.fm.version", version);
|
||||
|
||||
/* Set the mode for soc downloader */
|
||||
|
@ -199,9 +199,9 @@ initTavaruaRadio(hal::FMRadioSettings &aInfo)
|
|||
|
||||
/* Runs on the radio thread */
|
||||
static void *
|
||||
runTavaruaRadio(void *)
|
||||
runMsmFMRadio(void *)
|
||||
{
|
||||
initTavaruaRadio(sRadioSettings);
|
||||
initMsmFMRadio(sRadioSettings);
|
||||
if (!sRadioEnabled) {
|
||||
NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_ENABLE,
|
||||
hal::FM_RADIO_OPERATION_STATUS_FAIL));
|
||||
|
@ -275,7 +275,8 @@ EnableFMRadio(const hal::FMRadioSettings& aInfo)
|
|||
return;
|
||||
}
|
||||
|
||||
sTavaruaMode = !strcmp((char *)cap.driver, "radio-tavarua");
|
||||
sMsmFMMode = !strcmp((char *)cap.driver, "radio-tavarua") ||
|
||||
!strcmp((char *)cap.driver, "radio-iris");
|
||||
HAL_LOG(("Radio: %s (%s)\n", cap.driver, cap.card));
|
||||
|
||||
if (!(cap.capabilities & V4L2_CAP_RADIO)) {
|
||||
|
@ -289,10 +290,10 @@ EnableFMRadio(const hal::FMRadioSettings& aInfo)
|
|||
}
|
||||
sRadioSettings = aInfo;
|
||||
|
||||
if (sTavaruaMode) {
|
||||
if (sMsmFMMode) {
|
||||
sRadioFD = fd.forget();
|
||||
sTavaruaVersion = cap.version;
|
||||
pthread_create(&sRadioThread, nullptr, runTavaruaRadio, nullptr);
|
||||
sMsmFMVersion = cap.version;
|
||||
pthread_create(&sRadioThread, nullptr, runMsmFMRadio, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -323,7 +324,7 @@ DisableFMRadio()
|
|||
|
||||
sRadioEnabled = false;
|
||||
|
||||
if (sTavaruaMode) {
|
||||
if (sMsmFMMode) {
|
||||
int rc = setControl(V4L2_CID_PRIVATE_TAVARUA_STATE, FM_OFF);
|
||||
if (rc < 0) {
|
||||
HAL_LOG(("Unable to turn off radio"));
|
||||
|
@ -355,7 +356,7 @@ FMRadioSeek(const hal::FMRadioSeekDirection& aDirection)
|
|||
#endif
|
||||
|
||||
int rc = ioctl(sRadioFD, VIDIOC_S_HW_FREQ_SEEK, &seek);
|
||||
if (sTavaruaMode && rc >= 0)
|
||||
if (sMsmFMMode && rc >= 0)
|
||||
return;
|
||||
|
||||
hal::FMRadioOperationInformation info;
|
||||
|
@ -403,7 +404,7 @@ SetFMRadioFrequency(const uint32_t frequency)
|
|||
if (rc < 0)
|
||||
HAL_LOG(("Could not set radio frequency"));
|
||||
|
||||
if (sTavaruaMode && rc >= 0)
|
||||
if (sMsmFMMode && rc >= 0)
|
||||
return;
|
||||
|
||||
hal::FMRadioOperationInformation info;
|
||||
|
|
|
@ -186,6 +186,14 @@ PollSensors()
|
|||
if (buffer[i].type == SENSOR_TYPE_MAGNETIC_FIELD)
|
||||
continue;
|
||||
|
||||
// Bug 938035, transfer HAL data for orientation sensor to meet w3c spec
|
||||
// ex: HAL report alpha=90 means East but alpha=90 means West in w3c spec
|
||||
if (buffer[i].type == SENSOR_TYPE_ORIENTATION) {
|
||||
buffer[i].orientation.azimuth = 360 - buffer[i].orientation.azimuth;
|
||||
buffer[i].orientation.pitch = -buffer[i].orientation.pitch;
|
||||
buffer[i].orientation.roll = -buffer[i].orientation.roll;
|
||||
}
|
||||
|
||||
if (HardwareSensorToHalSensor(buffer[i].type) == SENSOR_UNKNOWN) {
|
||||
// Emulator is broken and gives us events without types set
|
||||
int index;
|
||||
|
|
|
@ -78,6 +78,7 @@ LOCAL_INCLUDES += -I$(srcdir)/../base \
|
|||
-I$(topsrcdir)/dom/audiochannel \
|
||||
-I$(topsrcdir)/dom/telephony \
|
||||
-I$(topsrcdir)/dom/media \
|
||||
-I$(topsrcdir)/dom/speakermanager \
|
||||
-I. \
|
||||
-I$(topsrcdir)/editor/libeditor/base \
|
||||
-I$(topsrcdir)/editor/libeditor/text \
|
||||
|
@ -100,6 +101,7 @@ endif
|
|||
|
||||
ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
|
||||
LOCAL_INCLUDES += -I$(topsrcdir)/dom/system/gonk
|
||||
LOCAL_INCLUDES += -I$(topsrcdir)/dom/speakermanager
|
||||
endif #}
|
||||
|
||||
ifdef MOZ_B2G_FM #{
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "nsVolumeService.h"
|
||||
#include "SpeakerManagerService.h"
|
||||
using namespace mozilla::system;
|
||||
#endif
|
||||
|
||||
|
@ -372,6 +373,7 @@ nsLayoutStatics::Shutdown()
|
|||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsVolumeService::Shutdown();
|
||||
SpeakerManagerService::Shutdown();
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WEBSPEECH
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче