merge fx-team to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2014-11-04 13:17:35 +01:00
commit ad0f429b28
37 changed files with 1013 additions and 328 deletions

View File

@ -182,6 +182,9 @@ pref("app.update.metro.enabled", true);
// If set to true, the Update Service will present no UI for any event.
pref("app.update.silent", false);
// If set to true, the hamburger button will show badges for update events.
pref("app.update.badge", false);
// If set to true, the Update Service will apply updates in the background
// when it finishes downloading them.
pref("app.update.staging.enabled", true);

View File

@ -1324,6 +1324,8 @@ var gBrowserInit = {
// Add Devtools menuitems and listeners
gDevToolsBrowser.registerBrowserWindow(window);
gMenuButtonUpdateBadge.init();
window.addEventListener("mousemove", MousePosTracker, false);
window.addEventListener("dragover", MousePosTracker, false);
@ -1470,6 +1472,8 @@ var gBrowserInit = {
DevEdition.uninit();
gMenuButtonUpdateBadge.uninit();
var enumerator = Services.wm.getEnumerator(null);
enumerator.getNext();
if (!enumerator.hasMoreElements()) {
@ -2439,6 +2443,118 @@ function PageProxyClickHandler(aEvent)
middleMousePaste(aEvent);
}
// Setup the hamburger button badges for updates, if enabled.
let gMenuButtonUpdateBadge = {
enabled: false,
init: function () {
try {
this.enabled = Services.prefs.getBoolPref("app.update.badge");
} catch (e) {}
if (this.enabled) {
PanelUI.menuButton.classList.add("badged-button");
Services.obs.addObserver(this, "update-staged", false);
}
},
uninit: function () {
if (this.enabled) {
Services.obs.removeObserver(this, "update-staged");
PanelUI.panel.removeEventListener("popupshowing", this, true);
this.enabled = false;
}
},
onMenuPanelCommand: function(event) {
if (event.originalTarget.getAttribute("update-status") === "succeeded") {
// restart the app
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
.createInstance(Ci.nsISupportsPRBool);
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
if (!cancelQuit.data) {
Services.startup.quit(Services.startup.eAttemptQuit | Services.startup.eRestart);
}
} else {
// open the page for manual update
let url = Services.urlFormatter.formatURLPref("app.update.url.manual");
openUILinkIn(url, "tab");
}
},
observe: function (subject, topic, status) {
const STATE_DOWNLOADING = "downloading";
const STATE_PENDING = "pending";
const STATE_PENDING_SVC = "pending-service";
const STATE_APPLIED = "applied";
const STATE_APPLIED_SVC = "applied-service";
const STATE_FAILED = "failed";
let updateButton = document.getElementById("PanelUI-update-status");
let updateButtonText;
let stringId;
// Update the UI when the background updater is finished.
switch (status) {
case STATE_APPLIED:
case STATE_APPLIED_SVC:
case STATE_PENDING:
case STATE_PENDING_SVC:
// If the update is successfully applied, or if the updater has fallen back
// to non-staged updates, add a badge to the hamburger menu to indicate an
// update will be applied once the browser restarts.
let badge = document.getAnonymousElementByAttribute(PanelUI.menuButton,
"class",
"toolbarbutton-badge");
badge.style.backgroundColor = 'green';
PanelUI.menuButton.setAttribute("badge", "\u2605");
let brandBundle = document.getElementById("bundle_brand");
let brandShortName = brandBundle.getString("brandShortName");
stringId = "appmenu.restartNeeded.description";
updateButtonText = gNavigatorBundle.getFormattedString(stringId,
[brandShortName]);
updateButton.label = updateButtonText;
updateButton.hidden = false;
updateButton.setAttribute("update-status", "succeeded");
PanelUI.panel.addEventListener("popupshowing", this, true);
break;
case STATE_FAILED:
// Background update has failed, let's show the UI responsible for
// prompting the user to update manually.
PanelUI.menuButton.setAttribute("badge", "!");
stringId = "appmenu.updateFailed.description";
updateButtonText = gNavigatorBundle.getString(stringId);
updateButton.label = updateButtonText;
updateButton.hidden = false;
updateButton.setAttribute("update-status", "failed");
PanelUI.panel.addEventListener("popupshowing", this, true);
break;
case STATE_DOWNLOADING:
// We've fallen back to downloading the full update because the partial
// update failed to get staged in the background. Therefore we need to keep
// our observer.
return;
}
this.uninit();
},
handleEvent: function(e) {
if (e.type === "popupshowing") {
PanelUI.menuButton.removeAttribute("badge");
PanelUI.panel.removeEventListener("popupshowing", this, true);
}
}
};
/**
* Handle command events bubbling up from error page content
* or from about:newtab or from remote error pages that invoke

View File

@ -213,12 +213,11 @@ let gSearch = {
if (uri) {
this._nodes.logo.style.backgroundImage = "url(" + uri + ")";
this._nodes.text.placeholder = "";
}
else {
this._nodes.logo.style.backgroundImage = "";
this._nodes.text.placeholder = engine.name;
}
this._nodes.text.placeholder = engine.placeholder;
// Set up the suggestion controller.
if (!this._suggestionController) {

View File

@ -140,7 +140,7 @@ skip-if = e10s
[browser_bug356571.js]
[browser_bug380960.js]
[browser_bug386835.js]
skip-if = e10s # Bug 691614 - no e10s zoom support yet
skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
[browser_bug405137.js]
[browser_bug406216.js]
[browser_bug409481.js]
@ -148,11 +148,11 @@ skip-if = e10s # Bug 691614 - no e10s zoom support yet
skip-if = e10s
[browser_bug413915.js]
[browser_bug416661.js]
skip-if = e10s # Bug 691614 - no e10s zoom support yet
skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
[browser_bug417483.js]
skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
[browser_bug419612.js]
skip-if = e10s # Bug 691614 - no e10s zoom support yet
skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
[browser_bug422590.js]
[browser_bug846489.js]
[browser_bug423833.js]
@ -168,7 +168,7 @@ skip-if = e10s # Bug ?????? - test directly manipulates content (eg, var expertD
[browser_bug435325.js]
skip-if = buildapp == 'mulet' || e10s # Bug ?????? - test directly manipulates content
[browser_bug441778.js]
skip-if = buildapp == 'mulet' || e10s # Bug 691614 - no e10s zoom support yet
skip-if = buildapp == 'mulet' || e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
[browser_bug455852.js]
skip-if = e10s
[browser_bug460146.js]
@ -176,11 +176,11 @@ skip-if = e10s # Bug 866413 - PageInfo doesn't work in e10s
[browser_bug462289.js]
skip-if = toolkit == "cocoa" || e10s # Bug ?????? - not sure why this is timing out and crashing!!
[browser_bug462673.js]
skip-if = e10s # Bug 924260 - "Window is closed"
skip-if = e10s # Bug 1093404 - test expects sync window opening from content and is disappointed in that expectation
[browser_bug477014.js]
skip-if = e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al for e10s
[browser_bug479408.js]
skip-if = buildapp == 'mulet' || e10s # Bug 918663 - DOMLinkAdded events don't make their way to chrome
skip-if = buildapp == 'mulet'
[browser_bug481560.js]
skip-if = e10s # Bug ????? - This bug attached an event listener directly to the content
[browser_bug484315.js]
@ -195,23 +195,20 @@ skip-if = e10s # Bug 866413 - PageInfo doesn't work in e10s
skip-if = e10s # Bug ?????? - some weird timing issue with progress listeners that fails intermittently
[browser_bug520538.js]
[browser_bug521216.js]
skip-if = e10s # Bug 918663 - DOMLinkAdded events don't make their way to chrome
[browser_bug533232.js]
[browser_bug537013.js]
skip-if = buildapp == 'mulet' || e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al for e10s (test calls replaceTabWithWindow)
[browser_bug537474.js]
skip-if = e10s # Bug ?????? - test doesn't wait for document to be created before it checks it
[browser_bug550565.js]
skip-if = e10s # Bug 918663 - DOMLinkAdded events don't make their way to chrome (which is how gBrowser.getIcon works)
[browser_bug553455.js]
skip-if = buildapp == 'mulet' || e10s # Bug 1066070 - I don't think either popup notifications nor addon install stuff works?
[browser_bug555224.js]
skip-if = e10s # Bug 691614 - no e10s zoom support yet
skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
[browser_bug555767.js]
skip-if = e10s # Bug 916974 - Session history doesn't work in e10s
skip-if = e10s # Bug 1093373 - relies on browser.sessionHistory
[browser_bug556061.js]
[browser_bug559991.js]
skip-if = e10s # Bug 691614 - no e10s zoom support yet
[browser_bug561623.js]
skip-if = e10s
[browser_bug561636.js]
@ -227,7 +224,7 @@ run-if = toolkit == "cocoa"
skip-if = e10s
[browser_bug575561.js]
[browser_bug575830.js]
skip-if = e10s # Bug 691614 - no e10s zoom support yet
skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
[browser_bug577121.js]
[browser_bug578534.js]
skip-if = e10s # Bug ?????? - test directly manipulates content
@ -260,7 +257,7 @@ skip-if = e10s # Bug ?????? - URLBar issues (apparently issues with redirection)
[browser_bug633691.js]
skip-if = e10s # Bug ?????? - test directly manipulates content (eg, var expertDiv = gBrowser.contentDocument.getElementById("expertContent");)
[browser_bug647886.js]
skip-if = buildapp == 'mulet' || e10s # Bug 916974 - Session history doesn't work in e10s
skip-if = buildapp == 'mulet' || e10s # Bug 1093373 - Relies on browser.sessionHistory
[browser_bug655584.js]
skip-if = e10s
[browser_bug664672.js]
@ -271,7 +268,7 @@ skip-if = e10s # Bug ?????? - Obscure non-windows failures ("Snapshot array has
[browser_bug710878.js]
skip-if = e10s # Bug ?????? - test directly manipulates content (doc.querySelector)
[browser_bug719271.js]
skip-if = e10s # Bug 691614 - no e10s zoom support yet
skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
[browser_bug724239.js]
skip-if = e10s # Bug 1077738
[browser_bug734076.js]
@ -287,7 +284,7 @@ skip-if = e10s # Bug ?????? - test reports a leaked nsGlobalWindow with e10s ena
skip-if = e10s # Bug ?????? - test directly manipulates content
[browser_bug783614.js]
[browser_bug816527.js]
skip-if = e10s # Bug 916974 - Session history doesn't work in e10s
skip-if = e10s # Bug 1093373 - relies on browser.sessionHistory
[browser_bug817947.js]
[browser_bug822367.js]
[browser_bug832435.js]
@ -301,7 +298,7 @@ skip-if = buildapp == "mulet" || e10s # Bug ?????? - test directly manipulates c
[browser_bug970746.js]
skip-if = e10s # Bug ?????? - test directly manipulates content (directly gets elements from the content)
[browser_bug1015721.js]
skip-if = os == 'win' || e10s # Bug 1056146 - FullZoomHelper uses promiseTabLoadEvent() which isn't e10s friendly
skip-if = os == 'win' || e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
[browser_bug1064280_changeUrlInPinnedTab.js]
[browser_canonizeURL.js]
skip-if = e10s # Bug ?????? - [JavaScript Error: "Error in AboutHome.sendAboutHomeData TypeError: target.messageManager is undefined" {file: "resource:///modules/AboutHome.jsm" line: 208}]
@ -321,7 +318,6 @@ skip-if = buildapp == 'mulet' || (os == "linux" && debug) || e10s # linux: bug 9
[browser_devices_get_user_media_about_urls.js]
skip-if = e10s # Bug 1071623
[browser_discovery.js]
skip-if = e10s # Bug 918663 - DOMLinkAdded events don't make their way to chrome
[browser_double_close_tab.js]
skip-if = e10s
[browser_duplicateIDs.js]

View File

@ -16,6 +16,10 @@
</vbox>
<footer id="PanelUI-footer">
<toolbarbutton id="PanelUI-update-status"
oncommand="gMenuButtonUpdateBadge.onMenuPanelCommand(event);"
wrap="true"
hidden="true"/>
<toolbarbutton id="PanelUI-fxa-status"
defaultlabel="&fxaSignIn.label;"
errorlabel="&fxaSignInError.label;"

View File

@ -27,6 +27,7 @@
window.OTProperties.configURL = window.OTProperties.assetURL + 'js/dynamic_config.min.js';
window.OTProperties.cssURL = window.OTProperties.assetURL + 'css/ot.css';
</script>
<script type="text/javascript" src="js/multiplexGum.js"></script>
<script type="text/javascript" src="shared/libs/sdk.js"></script>
<script type="text/javascript" src="libs/l10n-gaia-02ca67948fe8.js"></script>
<script type="text/javascript" src="shared/libs/react-0.11.2.js"></script>

View File

@ -0,0 +1,150 @@
/* 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/. */
var loop = loop || {};
/**
* Monkeypatch getUserMedia in a way that prevents additional camera and
* microphone prompts, at the cost of ignoring all constraints other than
* the first set passed in.
*
* The first call to navigator.getUserMedia (also now aliased to
* multiplexGum.getPermsAndCacheMedia to allow for explicit calling code)
* will cause the underlying gUM implementation to be called.
*
* While permission is pending, subsequent calls will result in the callbacks
* being queued. Once the call succeeds or fails, all queued success or
* failure callbacks will be invoked. Subsequent calls to either function will
* cause the success or failure callback to be invoked immediately.
*/
loop.standaloneMedia = (function() {
"use strict";
function patchSymbolIfExtant(objectName, propertyName, replacement) {
var object;
if (window[objectName]) {
object = window[objectName];
}
if (object && object[propertyName]) {
object[propertyName] = replacement;
}
}
// originalGum _must_ be on navigator; otherwise things blow up
navigator.originalGum = navigator.getUserMedia ||
navigator.mozGetUserMedia ||
navigator.webkitGetUserMedia ||
(window["TBPlugin"] && TBPlugin.getUserMedia);
function _MultiplexGum() {
this.reset();
}
_MultiplexGum.prototype = {
/**
* @see The docs at the top of this file for overall semantics,
* & http://developer.mozilla.org/en-US/docs/NavigatorUserMedia.getUserMedia
* for params, since this is intended to be purely a passthrough to gUM.
*/
getPermsAndCacheMedia: function(constraints, onSuccess, onError) {
function handleResult(callbacks, param) {
// Operate on a copy of the array in case any of the callbacks
// calls reset, which would cause an infinite-recursion.
this.userMedia.successCallbacks = [];
this.userMedia.errorCallbacks = [];
callbacks.forEach(function(cb) {
if (typeof cb == "function") {
cb(param);
}
})
}
function handleSuccess(localStream) {
this.userMedia.pending = false;
this.userMedia.localStream = localStream;
this.userMedia.error = null;
handleResult.call(this, this.userMedia.successCallbacks.slice(0), localStream);
}
function handleError(error) {
this.userMedia.pending = false;
this.userMedia.error = error;
handleResult.call(this, this.userMedia.errorCallbacks.slice(0), error);
this.error = null;
}
if (this.userMedia.localStream &&
this.userMedia.localStream.ended) {
this.userMedia.localStream = null;
}
this.userMedia.errorCallbacks.push(onError);
this.userMedia.successCallbacks.push(onSuccess);
if (this.userMedia.localStream) {
handleSuccess.call(this, this.userMedia.localStream);
return;
} else if (this.userMedia.error) {
handleError.call(this, this.userMedia.error);
return;
}
if (this.userMedia.pending) {
return;
}
this.userMedia.pending = true;
navigator.originalGum(constraints, handleSuccess.bind(this),
handleError.bind(this));
},
/**
* Reset the cached permissions, callbacks, and media to their default
* state and call any error callbacks to let any waiting callers know
* not to ever expect any more callbacks. We use "PERMISSION_DENIED",
* for lack of a better, more specific gUM code that callers are likely
* to be prepared to handle.
*/
reset: function() {
// When called from the ctor, userMedia is not created yet.
if (this.userMedia) {
this.userMedia.errorCallbacks.forEach(function(cb) {
if (typeof cb == "function") {
cb("PERMISSION_DENIED");
}
});
if (this.userMedia.localStream &&
typeof this.userMedia.localStream.stop == "function") {
this.userMedia.localStream.stop();
}
}
this.userMedia = {
error: null,
localStream: null,
pending: false,
errorCallbacks: [],
successCallbacks: [],
};
}
};
var singletonMultiplexGum = new _MultiplexGum();
function myGetUserMedia() {
// This function is needed to pull in the instance
// of the singleton for tests to overwrite the used instance.
singletonMultiplexGum.getPermsAndCacheMedia.apply(singletonMultiplexGum, arguments);
};
patchSymbolIfExtant("navigator", "mozGetUserMedia", myGetUserMedia);
patchSymbolIfExtant("navigator", "webkitGetUserMedia", myGetUserMedia);
patchSymbolIfExtant("navigator", "getUserMedia", myGetUserMedia);
patchSymbolIfExtant("TBPlugin", "getUserMedia", myGetUserMedia);
return {
multiplexGum: singletonMultiplexGum,
_MultiplexGum: _MultiplexGum,
setSingleton: function(singleton) {
singletonMultiplexGum = singleton;
},
};
})();

View File

@ -19,11 +19,14 @@ loop.webapp = (function($, _, OT, mozL10n) {
var sharedViews = loop.shared.views;
var sharedUtils = loop.shared.utils;
var multiplexGum = loop.standaloneMedia.multiplexGum;
/**
* Homepage view.
*/
var HomeView = React.createClass({displayName: 'HomeView',
render: function() {
loop.standaloneMedia.multiplexGum.reset();
return (
React.DOM.p(null, mozL10n.get("welcome", {clientShortname: mozL10n.get("clientShortname2")}))
);
@ -287,6 +290,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
},
_cancelOutgoingCall: function() {
loop.standaloneMedia.multiplexGum.reset();
this.props.websocket.cancel();
},
@ -448,8 +452,15 @@ loop.webapp = (function($, _, OT, mozL10n) {
*/
startCall: function(callType) {
return function() {
this.props.conversation.setupOutgoingCall(callType);
this.setState({disableCallButton: true});
multiplexGum.getPermsAndCacheMedia({audio:true, video:true},
function(localStream) {
this.props.conversation.setupOutgoingCall(callType);
this.setState({disableCallButton: true});
}.bind(this),
function(errorCode) {
multiplexGum.reset();
}.bind(this)
);
}.bind(this);
},

View File

@ -19,11 +19,14 @@ loop.webapp = (function($, _, OT, mozL10n) {
var sharedViews = loop.shared.views;
var sharedUtils = loop.shared.utils;
var multiplexGum = loop.standaloneMedia.multiplexGum;
/**
* Homepage view.
*/
var HomeView = React.createClass({
render: function() {
loop.standaloneMedia.multiplexGum.reset();
return (
<p>{mozL10n.get("welcome", {clientShortname: mozL10n.get("clientShortname2")})}</p>
);
@ -287,6 +290,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
},
_cancelOutgoingCall: function() {
loop.standaloneMedia.multiplexGum.reset();
this.props.websocket.cancel();
},
@ -448,8 +452,15 @@ loop.webapp = (function($, _, OT, mozL10n) {
*/
startCall: function(callType) {
return function() {
this.props.conversation.setupOutgoingCall(callType);
this.setState({disableCallButton: true});
multiplexGum.getPermsAndCacheMedia({audio:true, video:true},
function(localStream) {
this.props.conversation.setupOutgoingCall(callType);
this.setState({disableCallButton: true});
}.bind(this),
function(errorCode) {
multiplexGum.reset();
}.bind(this)
);
}.bind(this);
},

View File

@ -37,11 +37,13 @@
<script src="../../content/shared/js/views.js"></script>
<script src="../../content/shared/js/websocket.js"></script>
<script src="../../content/shared/js/feedbackApiClient.js"></script>
<script src="../../standalone/content/js/multiplexGum.js"></script>
<script src="../../standalone/content/js/standaloneClient.js"></script>
<script src="../../standalone/content/js/webapp.js"></script>
<!-- Test scripts -->
<script src="standalone_client_test.js"></script>
<script src="webapp_test.js"></script>
<script src="multiplexGum_test.js"></script>
<script>
mocha.run(function () {
$("#mocha").append("<p id='complete'>Complete.</p>");

View File

@ -0,0 +1,370 @@
/* 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/. */
/*global loop, sinon, it, beforeEach, afterEach, describe*/
var expect = chai.expect;
describe("loop.standaloneMedia._MultiplexGum", function() {
"use strict";
var defaultGum =
navigator.getUserMedia ||
navigator.mozGetUserMedia ||
navigator.webkitGetUserMedia ||
(window["TBPlugin"] && TBPlugin.getUserMedia);
var sandbox;
var multiplexGum;
beforeEach(function() {
sandbox = sinon.sandbox.create();
multiplexGum = new loop.standaloneMedia._MultiplexGum();
loop.standaloneMedia.setSingleton(multiplexGum);
});
afterEach(function() {
sandbox.restore();
});
describe("#constructor", function() {
it("pending should default to false", function() {
expect(multiplexGum.userMedia.pending).to.equal(false);
});
});
describe("default getUserMedia", function() {
it("should call getPermsAndCacheMedia", function() {
var fakeOptions = {audio: true, video: true};
var successCB = function() {};
var errorCB = function() {};
sandbox.stub(navigator, "originalGum");
sandbox.stub(loop.standaloneMedia._MultiplexGum.prototype,
"getPermsAndCacheMedia");
multiplexGum = new loop.standaloneMedia._MultiplexGum();
defaultGum(fakeOptions, successCB, errorCB);
sinon.assert.calledOnce(multiplexGum.getPermsAndCacheMedia);
sinon.assert.calledWithExactly(multiplexGum.getPermsAndCacheMedia,
fakeOptions, successCB, errorCB);
});
});
describe("#getPermsAndCacheMedia", function() {
beforeEach(function() {
sandbox.stub(navigator, "originalGum");
});
it("should change pending to true", function() {
multiplexGum.getPermsAndCacheMedia();
expect(multiplexGum.userMedia.pending).to.equal(true);
});
it("should call originalGum", function() {
multiplexGum.getPermsAndCacheMedia();
sinon.assert.calledOnce(navigator.originalGum);
});
it("should reset the pending state when the error callback is called",
function(done) {
var fakeError = new Error();
navigator.originalGum.callsArgWith(2, fakeError);
multiplexGum.getPermsAndCacheMedia(null, null, function onError(error) {
expect(multiplexGum.userMedia.pending).to.equal(false);
done();
});
});
it("should reset the pending state when the success callback is called",
function(done) {
var fakeLocalStream = {};
navigator.originalGum.callsArgWith(1, fakeLocalStream);
multiplexGum.getPermsAndCacheMedia(null,
function onSuccess(localStream) {
expect(multiplexGum.userMedia.pending).to.equal(false);
done();
}, null);
});
it("should call the error callback when originalGum calls back an error",
function(done) {
var fakeError = new Error();
navigator.originalGum.callsArgWith(2, fakeError);
multiplexGum.getPermsAndCacheMedia(null, null, function onError(error) {
expect(error).to.eql(fakeError);
done();
});
});
it("should propagate the success callback when originalGum succeeds",
function(done) {
var fakeLocalStream = {};
navigator.originalGum.callsArgWith(1, fakeLocalStream);
multiplexGum.getPermsAndCacheMedia(null,
function onSuccess(localStream) {
expect(localStream).to.eql(fakeLocalStream);
done();
}, null);
});
it("should call the success callback when the stream is cached",
function(done) {
var fakeLocalStream = {};
multiplexGum.userMedia.localStream = fakeLocalStream;
sinon.assert.notCalled(navigator.originalGum);
multiplexGum.getPermsAndCacheMedia(null,
function onSuccess(localStream) {
expect(localStream).to.eql(fakeLocalStream);
done();
}, null);
});
it("should call the error callback when an error is cached",
function(done) {
var fakeError = new Error();
multiplexGum.userMedia.error = fakeError;
sinon.assert.notCalled(navigator.originalGum);
multiplexGum.getPermsAndCacheMedia(null, null, function onError(error) {
expect(error).to.eql(fakeError);
done();
});
});
it("should clear the error when success is called back", function(done) {
var fakeError = new Error();
var fakeLocalStream = {};
multiplexGum.userMedia.localStream = fakeLocalStream;
multiplexGum.userMedia.error = fakeError;
multiplexGum.getPermsAndCacheMedia(null, function onSuccess(localStream) {
expect(multiplexGum.userMedia.error).to.not.eql(fakeError);
expect(localStream).to.eql(fakeLocalStream);
done();
}, null);
});
it("should call all success callbacks when success is achieved",
function(done) {
var fakeLocalStream = {};
var calls = 0;
// Async is needed so that the callbacks can be queued up.
navigator.originalGum.callsArgWithAsync(1, fakeLocalStream);
multiplexGum.getPermsAndCacheMedia(null, function onSuccess(localStream) {
calls += 1;
expect(localStream).to.eql(fakeLocalStream);
}, null);
expect(multiplexGum.userMedia).to.have.property('pending', true);
multiplexGum.getPermsAndCacheMedia(null, function onSuccess(localStream) {
calls += 10;
expect(localStream).to.eql(fakeLocalStream);
expect(calls).to.equal(11);
done();
}, null);
});
it("should call all error callbacks when error is encountered",
function(done) {
var fakeError = new Error();
var calls = 0;
// Async is needed so that the callbacks can be queued up.
navigator.originalGum.callsArgWithAsync(2, fakeError);
multiplexGum.getPermsAndCacheMedia(null, null, function onError(error) {
calls += 1;
expect(error).to.eql(fakeError);
});
expect(multiplexGum.userMedia).to.have.property('pending', true);
multiplexGum.getPermsAndCacheMedia(null, null, function onError(error) {
calls += 10;
expect(error).to.eql(fakeError);
expect(calls).to.eql(11);
done();
});
});
it("should not call a getPermsAndCacheMedia success callback at the time" +
" of gUM success callback fires",
function(done) {
var fakeLocalStream = {};
multiplexGum.userMedia.localStream = fakeLocalStream;
navigator.originalGum.callsArgWith(1, fakeLocalStream);
var calledOnce = false;
var promiseCalledOnce = new Promise(function(resolve, reject) {
multiplexGum.getPermsAndCacheMedia(null,
function gPACMSuccess(localStream) {
expect(localStream).to.eql(fakeLocalStream);
expect(multiplexGum.userMedia).to.have.property('pending', false);
expect(multiplexGum.userMedia.successCallbacks.length).to.equal(0);
if (calledOnce) {
sinon.assert.fail("original callback was called twice");
}
calledOnce = true;
resolve();
}, function() {
sinon.assert.fail("error callback should not have fired");
reject();
done();
});
});
promiseCalledOnce.then(function() {
defaultGum(null, function gUMSuccess(localStream2) {
expect(localStream2).to.eql(fakeLocalStream);
expect(multiplexGum.userMedia).to.have.property('pending', false);
expect(multiplexGum.userMedia.successCallbacks.length).to.equal(0);
done();
});
});
});
it("should not call a getPermsAndCacheMedia error callback when the " +
" gUM error callback fires",
function(done) {
var fakeError = "monkeys ate the stream";
multiplexGum.userMedia.error = fakeError;
navigator.originalGum.callsArgWith(2, fakeError);
var calledOnce = false;
var promiseCalledOnce = new Promise(function(resolve, reject) {
multiplexGum.getPermsAndCacheMedia(null, function() {
sinon.assert.fail("success callback should not have fired");
reject();
done();
}, function gPACMError(errString) {
expect(errString).to.eql(fakeError);
expect(multiplexGum.userMedia).to.have.property('pending', false);
if (calledOnce) {
sinon.assert.fail("original error callback was called twice");
}
calledOnce = true;
resolve();
});
});
promiseCalledOnce.then(function() {
defaultGum(null, function() {},
function gUMError(errString) {
expect(errString).to.eql(fakeError);
expect(multiplexGum.userMedia).to.have.property('pending', false);
done();
});
});
});
it("should call the success callback with a new stream, " +
" when a new stream is available",
function(done) {
var endedStream = {ended: true};
var newStream = {};
multiplexGum.userMedia.localStream = endedStream;
navigator.originalGum.callsArgWith(1, newStream);
multiplexGum.getPermsAndCacheMedia(null, function onSuccess(localStream) {
expect(localStream).to.eql(newStream);
done();
}, null);
});
});
describe("#reset", function () {
it("should reset all userMedia state to default", function() {
// If userMedia is defined, then it needs to have all of
// the properties that multipleGum will depend on. It is
// easier to simply delete the object than to setup a fake
// state of the object.
delete multiplexGum.userMedia;
multiplexGum.reset();
expect(multiplexGum.userMedia).to.deep.equal({
error: null,
localStream: null,
pending: false,
errorCallbacks: [],
successCallbacks: [],
});
});
it("should call all queued error callbacks with 'PERMISSION_DENIED'",
function(done) {
sandbox.stub(navigator, "originalGum");
multiplexGum.getPermsAndCacheMedia(null, function(localStream) {
sinon.assert.fail(
"The success callback shouldn't be called due to reset");
}, function(error) {
expect(error).to.equal("PERMISSION_DENIED");
done();
});
multiplexGum.reset();
});
it("should call MST.stop() on the stream tracks", function() {
var stopStub = sandbox.stub();
multiplexGum.userMedia.localStream = {stop: stopStub};
multiplexGum.reset();
sinon.assert.calledOnce(stopStub);
});
it("should not call MST.stop() on the stream tracks if .stop() doesn't exist",
function() {
multiplexGum.userMedia.localStream = {};
try {
multiplexGum.reset();
} catch (ex) {
sinon.assert.fail(
"reset shouldn't throw when a stream doesn't implement stop(): "
+ ex);
}
});
it("should not get stuck in recursion if the error callback calls 'reset'",
function() {
sandbox.stub(navigator, "originalGum");
navigator.originalGum.callsArgWith(2, "PERMISSION_DENIED");
var calledOnce = false;
multiplexGum.getPermsAndCacheMedia(null, null, function() {
if (calledOnce) {
sinon.assert.fail("reset should only be called once");
}
calledOnce = true;
multiplexGum.reset.bind(multiplexGum)();
});
});
it("should not get stuck in recursion if the success callback calls 'reset'",
function() {
sandbox.stub(navigator, "originalGum");
navigator.originalGum.callsArgWith(1, {});
var calledOnce = false;
multiplexGum.getPermsAndCacheMedia(null, function() {
calledOnce = true;
multiplexGum.reset.bind(multiplexGum)();
}, function() {
if (calledOnce) {
sinon.assert.fail("reset should only be called once");
}
calledOnce = true;
});
});
});
});

View File

@ -13,9 +13,11 @@ describe("loop.webapp", function() {
var sharedModels = loop.shared.models,
sharedViews = loop.shared.views,
sharedUtils = loop.shared.utils,
standaloneMedia = loop.standaloneMedia,
sandbox,
notifications,
feedbackApiClient;
feedbackApiClient,
stubGetPermsAndCacheMedia;
beforeEach(function() {
sandbox = sinon.sandbox.create();
@ -23,6 +25,9 @@ describe("loop.webapp", function() {
feedbackApiClient = new loop.FeedbackAPIClient("http://invalid", {
product: "Loop"
});
stubGetPermsAndCacheMedia = sandbox.stub(
loop.standaloneMedia._MultiplexGum.prototype, "getPermsAndCacheMedia");
});
afterEach(function() {
@ -610,6 +615,19 @@ describe("loop.webapp", function() {
});
});
describe("HomeView", function() {
it("should call loop.standaloneMedia.reset", function() {
var multiplexGum = new standaloneMedia._MultiplexGum();
standaloneMedia.setSingleton(multiplexGum);
sandbox.stub(standaloneMedia._MultiplexGum.prototype, "reset");
TestUtils.renderIntoDocument(loop.webapp.HomeView());
sinon.assert.calledOnce(multiplexGum.reset);
sinon.assert.calledWithExactly(multiplexGum.reset);
});
});
describe("PendingConversationView", function() {
var view, websocket, fakeAudio;
@ -652,6 +670,18 @@ describe("loop.webapp", function() {
sinon.assert.calledOnce(websocket.cancel);
});
it("should call multiplexGum.reset to release the camera", function() {
var multiplexGum = new standaloneMedia._MultiplexGum();
standaloneMedia.setSingleton(multiplexGum);
sandbox.stub(standaloneMedia._MultiplexGum.prototype, "reset");
var button = view.getDOMNode().querySelector(".btn-cancel");
React.addons.TestUtils.Simulate.click(button);
sinon.assert.calledOnce(multiplexGum.reset);
sinon.assert.calledWithExactly(multiplexGum.reset);
});
});
describe("Events", function() {
@ -697,8 +727,27 @@ describe("loop.webapp", function() {
client: standaloneClientStub
})
);
// default to succeeding with a null local media object
stubGetPermsAndCacheMedia.callsArgWith(1, {});
});
it("should fire multiplexGum.reset when getPermsAndCacheMedia calls" +
" back an error",
function() {
var setupOutgoingCall = sinon.stub(conversation, "setupOutgoingCall");
var multiplexGum = new standaloneMedia._MultiplexGum();
standaloneMedia.setSingleton(multiplexGum);
sandbox.stub(standaloneMedia._MultiplexGum.prototype, "reset");
stubGetPermsAndCacheMedia.callsArgWith(2, "FAKE_ERROR");
var button = view.getDOMNode().querySelector(".btn-accept");
React.addons.TestUtils.Simulate.click(button);
sinon.assert.calledOnce(multiplexGum.reset);
sinon.assert.calledWithExactly(multiplexGum.reset);
});
it("should start the audio-video conversation establishment process",
function() {
var setupOutgoingCall = sinon.stub(conversation, "setupOutgoingCall");
@ -1002,6 +1051,9 @@ describe("loop.webapp", function() {
client: standaloneClientStub
})
);
// default to succeeding with a null local media object
stubGetPermsAndCacheMedia.callsArgWith(1, {});
});
it("should start the conversation establishment process", function() {

View File

@ -26,6 +26,7 @@
window.OTProperties.configURL = window.OTProperties.assetURL + 'js/dynamic_config.min.js';
window.OTProperties.cssURL = window.OTProperties.assetURL + 'css/ot.css';
</script>
<script src="../content/js/multiplexGum.js"></script>
<script src="../content/shared/libs/sdk.js"></script>
<script src="../content/shared/libs/react-0.11.2.js"></script>
<script src="../content/shared/libs/jquery-2.1.0.js"></script>

View File

@ -413,14 +413,52 @@ BrowserGlue.prototype = {
case "nsPref:changed":
if (data == POLARIS_ENABLED) {
let enabled = Services.prefs.getBoolPref(POLARIS_ENABLED);
Services.prefs.setBoolPref("privacy.donottrackheader.enabled", enabled);
Services.prefs.setBoolPref("privacy.trackingprotection.enabled", enabled);
Services.prefs.setBoolPref("privacy.trackingprotection.ui.enabled", enabled);
if (enabled) {
let e10sEnabled = Services.appinfo.browserTabsRemoteAutostart;
let shouldRestart = e10sEnabled && this._promptForE10sRestart();
// Only set the related prefs if e10s is not enabled or the user
// saw a notification that e10s would be disabled on restart.
if (!e10sEnabled || shouldRestart) {
Services.prefs.setBoolPref("privacy.donottrackheader.enabled", enabled);
Services.prefs.setBoolPref("privacy.trackingprotection.enabled", enabled);
Services.prefs.setBoolPref("privacy.trackingprotection.ui.enabled", enabled);
if (shouldRestart) {
Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit |
Ci.nsIAppStartup.eRestart);
}
} else {
// The user chose not to disable E10s which is temporarily
// incompatible with Polaris.
Services.prefs.clearUserPref(POLARIS_ENABLED);
}
} else {
// Don't reset DNT because its visible pref is independent of
// Polaris and may have been previously set.
Services.prefs.clearUserPref("privacy.trackingprotection.enabled");
Services.prefs.clearUserPref("privacy.trackingprotection.ui.enabled");
}
}
#endif
}
},
_promptForE10sRestart: function () {
let win = this.getMostRecentBrowserWindow();
let brandBundle = win.document.getElementById("bundle_brand");
let brandName = brandBundle.getString("brandShortName");
let prefBundle = win.document.getElementById("bundle_preferences");
let msg = "Multiprocess Nightly (e10s) does not yet support tracking protection. Multiprocessing will be disabled if you restart Firefox. Would you like to continue?";
let title = prefBundle.getFormattedString("shouldRestartTitle", [brandName]);
let shouldRestart = Services.prompt.confirm(win, title, msg);
if (shouldRestart) {
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
.createInstance(Ci.nsISupportsPRBool);
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
shouldRestart = !cancelQuit.data;
}
return shouldRestart;
},
_syncSearchEngines: function () {
// Only do this if the search service is already initialized. This function
// gets called in finalUIStartup and from a browser-search-service observer,

View File

@ -4,3 +4,4 @@
skip-if = e10s # Bug ?????? - child process crash, but only when run as part of the suite (ie, probably not actually this tests fault!?)
[browser_polaris_prefs.js]
skip-if = e10s # Bug 1089774 - Tracking protection and e10s are incompatible.

View File

@ -45,7 +45,12 @@ add_task(function* test_changing_pref_changes_tracking() {
Services.prefs.setBoolPref(POLARIS_ENABLED, true);
yield assertPref(pref, true);
Services.prefs.setBoolPref(POLARIS_ENABLED, false);
yield assertPref(pref, false);
// We don't clear the DNT pref if Polaris is disabled.
if (pref != PREF_DNT) {
yield assertPref(pref, false);
} else {
yield assertPref(pref, true);
}
Services.prefs.setBoolPref(POLARIS_ENABLED, true);
yield assertPref(pref, true);
}
@ -63,8 +68,10 @@ add_task(function* test_prefs_can_be_changed_individually() {
Services.prefs.setBoolPref(pref, false);
yield assertPref(pref, false);
yield assertPref(POLARIS_ENABLED, true);
Services.prefs.setBoolPref(POLARIS_ENABLED, false);
yield assertPref(pref, false);
Services.prefs.setBoolPref(pref, true);
yield assertPref(pref, true);
yield assertPref(POLARIS_ENABLED, false);

View File

@ -19,7 +19,7 @@ window.addEventListener("load", function onLoad() {
// Buttons
document.querySelector("#close").onclick = CloseUI;
document.querySelector("#restore").onclick = RestoreDefaults;
document.querySelector("#manageSimulators").onclick = ShowAddons;
document.querySelector("#manageComponents").onclick = ShowAddons;
// Initialize the controls
FillForm();

View File

@ -20,7 +20,7 @@
<div id="controls">
<a id="restore">&prefs_restore;</a>
<a id="manageSimulators">&prefs_simulators;</a>
<a id="manageComponents">&prefs_manage_components;</a>
<a id="close">&deck_close;</a>
</div>

View File

@ -68,6 +68,7 @@
<menuitem command="cmd_removeProject" accesskey="&projectMenu_remove_accesskey;"/>
<menuseparator/>
<menuitem command="cmd_showPrefs" label="&projectMenu_showPrefs_label;" accesskey="&projectMenu_showPrefs_accesskey;"/>
<menuitem command="cmd_showAddons" label="&projectMenu_manageComponents_label;" accesskey="&projectMenu_manageComponents_accesskey;"/>
</menupopup>
</menu>
@ -85,7 +86,6 @@
<menu id="menu-view" label="&viewMenu_label;" accesskey="&viewMenu_accesskey;">
<menupopup id="menu-ViewPopup">
<menuitem command="cmd_toggleEditor" key="key_toggleEditor" accesskey="&viewMenu_toggleEditor_accesskey;"/>
<menuitem command="cmd_showAddons" label="&viewMenu_showAddons_label;" accesskey="&viewMenu_showAddons_accesskey;"/>
</menupopup>
</menu>

View File

@ -24,6 +24,8 @@
<!ENTITY projectMenu_remove_accesskey "R">
<!ENTITY projectMenu_showPrefs_label "Preferences">
<!ENTITY projectMenu_showPrefs_accesskey "e">
<!ENTITY projectMenu_manageComponents_label "Manage Extra Components">
<!ENTITY projectMenu_manageComponents_accesskey "M">
<!ENTITY runtimeMenu_label "Runtime">
<!ENTITY runtimeMenu_accesskey "R">
@ -42,8 +44,6 @@
<!ENTITY viewMenu_accesskey "V">
<!ENTITY viewMenu_toggleEditor_label "Toggle Editor">
<!ENTITY viewMenu_toggleEditor_accesskey "E">
<!ENTITY viewMenu_showAddons_label "Manage Simulators">
<!ENTITY viewMenu_showAddons_accesskey "M">
<!ENTITY projectButton_label "Open App">
<!ENTITY runtimeButton_label "Select Runtime">
@ -100,7 +100,7 @@
<!ENTITY prefs_editor_title "Editor">
<!ENTITY prefs_general_title "General">
<!ENTITY prefs_restore "Restore Defaults">
<!ENTITY prefs_simulators "Manage Simulators">
<!ENTITY prefs_manage_components "Manage Extra Components">
<!ENTITY prefs_options_rememberlastproject "Remember last project">
<!ENTITY prefs_options_rememberlastproject_tooltip "Restore previous project when WebIDE starts">
<!ENTITY prefs_options_templatesurl "Templates URL">

View File

@ -96,6 +96,7 @@ this.ContentSearch = {
addMessageListener(INBOUND_MESSAGE, this);
Services.obs.addObserver(this, "browser-search-engine-modified", false);
Services.obs.addObserver(this, "shutdown-leaks-before-check", false);
this._stringBundle = Services.strings.createBundle("chrome://global/locale/autocomplete.properties");
},
destroy: function () {
@ -391,8 +392,11 @@ this.ContentSearch = {
let favicon = engine.getIconURLBySize(16, 16);
let uri1x = engine.getIconURLBySize(65, 26);
let uri2x = engine.getIconURLBySize(130, 52);
let placeholder = this._stringBundle.formatStringFromName(
"searchWithEngine", [engine.name], 1);
let obj = {
name: engine.name,
placeholder: placeholder,
iconBuffer: yield this._arrayBufferFromDataURI(favicon),
logoBuffer: yield this._arrayBufferFromDataURI(uri1x),
logo2xBuffer: yield this._arrayBufferFromDataURI(uri2x),

View File

@ -388,8 +388,10 @@ let currentEngineObj = Task.async(function* () {
let uri1x = engine.getIconURLBySize(65, 26);
let uri2x = engine.getIconURLBySize(130, 52);
let uriFavicon = engine.getIconURLBySize(16, 16);
let bundle = Services.strings.createBundle("chrome://global/locale/autocomplete.properties");
return {
name: engine.name,
placeholder: bundle.formatStringFromName("searchWithEngine", [engine.name], 1),
logoBuffer: yield arrayBufferFromDataURI(uri1x),
logo2xBuffer: yield arrayBufferFromDataURI(uri2x),
iconBuffer: yield arrayBufferFromDataURI(uriFavicon),

View File

@ -1413,6 +1413,7 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-ic
-moz-image-region: rect(0px, 192px, 32px, 160px);
}
#PanelUI-update-status > .toolbarbutton-icon,
#PanelUI-fxa-status > .toolbarbutton-icon,
#PanelUI-quit > .toolbarbutton-icon,
#PanelUI-customize > .toolbarbutton-icon,

View File

@ -22,6 +22,10 @@
linear-gradient(rgba(255,255,255,0.3), transparent);
}
#PanelUI-update-status {
list-style-image: url(chrome://branding/content/icon32.png);
}
#PanelUI-fxa-status {
list-style-image: url(chrome://browser/skin/sync-horizontalbar@2x.png);
}

View File

@ -381,6 +381,7 @@ toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"] > iframe {
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .panel-wide-item,
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .toolbarbutton-1:not([panel-multiview-anchor="true"]),
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-fxa-status,
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-update-status,
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > toolbarseparator,
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-customize,
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-help:not([panel-multiview-anchor="true"]) {
@ -493,6 +494,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
margin: 0;
}
#PanelUI-update-status,
#PanelUI-help,
#PanelUI-fxa-status,
#PanelUI-customize,
@ -509,12 +511,14 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
-moz-box-orient: horizontal;
}
#PanelUI-update-status,
#PanelUI-fxa-status {
border-top: 1px solid hsla(210,4%,10%,.14);
border-bottom: 1px solid transparent;
margin-bottom: -1px;
}
#PanelUI-update-status > .toolbarbutton-text,
#PanelUI-fxa-status > .toolbarbutton-text {
width: 0; /* Fancy cropping solution for flexbox. */
}
@ -524,6 +528,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
min-width: 46px;
}
#PanelUI-update-status > .toolbarbutton-text,
#PanelUI-fxa-status > .toolbarbutton-text,
#PanelUI-customize > .toolbarbutton-text {
margin: 0;
@ -536,6 +541,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
display: none;
}
#PanelUI-update-status > .toolbarbutton-icon,
#PanelUI-fxa-status > .toolbarbutton-icon,
#PanelUI-customize > .toolbarbutton-icon,
#PanelUI-help > .toolbarbutton-icon,
@ -550,6 +556,16 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
-moz-border-start-style: none;
}
#PanelUI-update-status {
width: calc(@menuPanelWidth@ + 30px);
-moz-padding-start: 15px;
-moz-border-start-style: none;
}
#PanelUI-update-status {
list-style-image: url(chrome://branding/content/icon16.png);
}
#PanelUI-fxa-status {
list-style-image: url(chrome://browser/skin/sync-horizontalbar.png);
}
@ -625,6 +641,32 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
outline: none;
}
#PanelUI-update-status[update-status="succeeded"] {
background-color: hsla(96, 65%, 75%, 0.1);
}
#PanelUI-update-status[update-status="succeeded"]:not([disabled]):hover {
background-color: hsla(96, 65%, 75%, 0.4);
}
#PanelUI-update-status[update-status="succeeded"]:not([disabled]):hover:active {
background-color: hsla(96, 65%, 75%, 0.6);
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}
#PanelUI-update-status[update-status="failed"] {
background-color: hsla(359, 69%, 84%, 0.1);
}
#PanelUI-update-status[update-status="failed"]:not([disabled]):hover {
background-color: hsla(359, 69%, 84%, 0.4);
}
#PanelUI-update-status[update-status="failed"]:not([disabled]):hover:active {
background-color: hsla(359, 69%, 84%, 0.6);
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}
#PanelUI-quit:not([disabled]):hover {
background-color: #d94141;
outline-color: #c23a3a;

View File

@ -412,26 +412,6 @@ case "$target" in
;;
esac
MOZ_ARG_DISABLE_BOOL(android-include-fonts,
[ --disable-android-include-fonts
disable the inclusion of fonts into the final APK],
MOZ_ANDROID_EXCLUDE_FONTS=1)
if test -n "$MOZ_ANDROID_EXCLUDE_FONTS"; then
AC_DEFINE(MOZ_ANDROID_EXCLUDE_FONTS, $MOZ_ANDROID_EXCLUDE_FONTS)
AC_SUBST(MOZ_ANDROID_EXCLUDE_FONTS)
fi
MOZ_ARG_ENABLE_BOOL(android-resource-constrained,
[ --enable-android-resource-constrained
exclude hi-res images and similar from the final APK],
MOZ_ANDROID_RESOURCE_CONSTRAINED=1)
if test -n "$MOZ_ANDROID_RESOURCE_CONSTRAINED"; then
AC_DEFINE(MOZ_ANDROID_RESOURCE_CONSTRAINED, $MOZ_ANDROID_RESOURCE_CONSTRAINED)
AC_SUBST(MOZ_ANDROID_RESOURCE_CONSTRAINED)
fi
MOZ_ARG_WITH_STRING(android-min-sdk,
[ --with-android-min-sdk=[VER] Impose a minimum Firefox for Android SDK version],
[ MOZ_ANDROID_MIN_SDK_VERSION=$withval ])

View File

@ -4094,6 +4094,30 @@ fi
AC_SUBST(MOZ_BING_API_CLIENTID)
AC_SUBST(MOZ_BING_API_KEY)
# Whether to include optional-but-large font files in the final APK.
# We want this in mobile/android/confvars.sh, so it goes early.
MOZ_ARG_DISABLE_BOOL(android-include-fonts,
[ --disable-android-include-fonts
Disable the inclusion of fonts into the final APK],
MOZ_ANDROID_EXCLUDE_FONTS=1)
if test -n "$MOZ_ANDROID_EXCLUDE_FONTS"; then
AC_DEFINE(MOZ_ANDROID_EXCLUDE_FONTS)
fi
AC_SUBST(MOZ_ANDROID_EXCLUDE_FONTS)
# Whether this APK is destined for resource constrained devices.
# We want this in mobile/android/confvars.sh, so it goes early.
MOZ_ARG_ENABLE_BOOL(android-resource-constrained,
[ --enable-android-resource-constrained
Exclude hi-res images and similar from the final APK],
MOZ_ANDROID_RESOURCE_CONSTRAINED=1)
if test -n "$MOZ_ANDROID_RESOURCE_CONSTRAINED"; then
AC_DEFINE(MOZ_ANDROID_RESOURCE_CONSTRAINED)
fi
AC_SUBST(MOZ_ANDROID_RESOURCE_CONSTRAINED)
# Allow the application to influence configure with a confvars.sh script.
AC_MSG_CHECKING([if app-specific confvars.sh exists])
if test -f "${srcdir}/${MOZ_BUILD_APP}/confvars.sh" ; then

View File

@ -48,6 +48,25 @@ class ChromeCast implements GeckoMediaPlayer {
private MirrorChannel mMirrorChannel;
private boolean mApplicationStarted = false;
// EventCallback which is actually a GeckoEventCallback is sometimes being invoked more
// than once. That causes the IllegalStateException to be thrown. To prevent a crash,
// catch the exception and report it as an error to the log.
private static void sendSuccess(final EventCallback callback, final String msg) {
try {
callback.sendSuccess(msg);
} catch (final IllegalStateException e) {
Log.e(LOGTAG, "Attempting to invoke callback.sendSuccess more than once.", e);
}
}
private static void sendError(final EventCallback callback, final String msg) {
try {
callback.sendError(msg);
} catch (final IllegalStateException e) {
Log.e(LOGTAG, "Attempting to invoke callback.sendError more than once.", e);
}
}
// Callback to start playback of a url on a remote device
private class VideoPlayCallback implements ResultCallback<ApplicationConnectionResult>,
RemoteMediaPlayer.OnStatusUpdatedListener,
@ -101,7 +120,7 @@ class ChromeCast implements GeckoMediaPlayer {
startPlayback();
} else {
callback.sendError(status.toString());
sendError(callback, status.toString());
}
}
@ -118,13 +137,13 @@ class ChromeCast implements GeckoMediaPlayer {
@Override
public void onResult(MediaChannelResult result) {
if (result.getStatus().isSuccess()) {
callback.sendSuccess(null);
sendSuccess(callback, null);
debug("Media loaded successfully");
return;
}
debug("Media load failed " + result.getStatus());
callback.sendError(result.getStatus().toString());
sendError(callback, result.getStatus().toString());
}
});
@ -135,7 +154,7 @@ class ChromeCast implements GeckoMediaPlayer {
debug("Problem opening media during loading", e);
}
callback.sendError("");
sendError(callback, "");
}
}
@ -198,7 +217,7 @@ class ChromeCast implements GeckoMediaPlayer {
// Sometimes apiClient is null here. See bug 1061032
if (apiClient != null && !apiClient.isConnected()) {
debug("Connection failed");
callback.sendError("Not connected");
sendError(callback, "Not connected");
return;
}
@ -223,13 +242,13 @@ class ChromeCast implements GeckoMediaPlayer {
@Override
public void start(final EventCallback callback) {
// Nothing to be done here
callback.sendSuccess(null);
sendSuccess(callback, null);
}
@Override
public void stop(final EventCallback callback) {
// Nothing to be done here
callback.sendSuccess(null);
sendSuccess(callback, null);
}
public boolean verifySession(final EventCallback callback) {
@ -245,7 +264,7 @@ class ChromeCast implements GeckoMediaPlayer {
if (msg != null) {
debug(msg);
if (callback != null) {
callback.sendError(msg);
sendError(callback, msg);
}
return false;
}
@ -266,15 +285,15 @@ class ChromeCast implements GeckoMediaPlayer {
Status status = result.getStatus();
if (!status.isSuccess()) {
debug("Unable to play: " + status.getStatusCode());
callback.sendError(status.toString());
sendError(callback, status.toString());
} else {
callback.sendSuccess(null);
sendSuccess(callback, null);
}
}
});
} catch(IllegalStateException ex) {
// The media player may throw if the session has been killed. For now, we're just catching this here.
callback.sendError("Error playing");
sendError(callback, "Error playing");
}
}
@ -291,15 +310,15 @@ class ChromeCast implements GeckoMediaPlayer {
Status status = result.getStatus();
if (!status.isSuccess()) {
debug("Unable to pause: " + status.getStatusCode());
callback.sendError(status.toString());
sendError(callback, status.toString());
} else {
callback.sendSuccess(null);
sendSuccess(callback, null);
}
}
});
} catch(IllegalStateException ex) {
// The media player may throw if the session has been killed. For now, we're just catching this here.
callback.sendError("Error pausing");
sendError(callback, "Error pausing");
}
}
@ -322,7 +341,7 @@ class ChromeCast implements GeckoMediaPlayer {
apiClient = null;
if (callback != null) {
callback.sendSuccess(null);
sendSuccess(callback, null);
}
return;
@ -332,13 +351,13 @@ class ChromeCast implements GeckoMediaPlayer {
}
if (callback != null) {
callback.sendError(result.getStatus().toString());
sendError(callback, result.getStatus().toString());
}
}
});
} catch(IllegalStateException ex) {
// The media player may throw if the session has been killed. For now, we're just catching this here.
callback.sendError("Error stopping");
sendError(callback, "Error stopping");
}
}
@ -376,9 +395,7 @@ class ChromeCast implements GeckoMediaPlayer {
}
}
private class MirrorCallback implements ResultCallback<ApplicationConnectionResult> {
// See Bug 1055562, callback is set to null after it has been
// invoked so that it will not be called a second time.
EventCallback callback;
final EventCallback callback;
MirrorCallback(final EventCallback callback) {
this.callback = callback;
}
@ -386,10 +403,6 @@ class ChromeCast implements GeckoMediaPlayer {
@Override
public void onResult(ApplicationConnectionResult result) {
if (callback == null) {
Log.e(LOGTAG, "Attempting to invoke MirrorChannel callback more than once.");
return;
}
Status status = result.getStatus();
if (status.isSuccess()) {
ApplicationMetadata applicationMetadata = result.getApplicationMetadata();
@ -406,16 +419,14 @@ class ChromeCast implements GeckoMediaPlayer {
mMirrorChannel
.getNamespace(),
mMirrorChannel);
callback.sendSuccess(null);
callback = null;
sendSuccess(callback, null);
} catch (IOException e) {
Log.e(LOGTAG, "Exception while creating channel", e);
}
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Casting:Mirror", route.getId()));
} else {
callback.sendError(status.toString());
callback = null;
sendError(callback, status.toString());
}
}
}

View File

@ -13,12 +13,15 @@ var WebcompatReporter = {
menuItemEnabled: null,
init: function() {
Services.obs.addObserver(this, "DesktopMode:Change", false);
Services.obs.addObserver(this, "content-page-shown", false);
Services.obs.addObserver(this, "chrome-document-global-created", false);
Services.obs.addObserver(this, "content-document-global-created", false);
this.addMenuItem();
},
uninit: function() {
Services.obs.removeObserver(this, "DesktopMode:Change");
Services.obs.removeObserver(this, "chrome-document-global-created");
Services.obs.removeObserver(this, "content-document-global-created");
if (this.menuItem) {
NativeWindow.menu.remove(this.menuItem);
@ -27,8 +30,15 @@ var WebcompatReporter = {
},
observe: function(subject, topic, data) {
if (topic === "content-page-shown") {
let currentURI = subject.documentURI;
if (topic == "content-document-global-created" || topic == "chrome-document-global-created") {
let win = subject;
let currentURI = win.document.documentURI;
// Ignore non top-level documents
if (currentURI !== win.top.location.href) {
return;
}
if (!this.menuItemEnabled && this.isReportableUrl(currentURI)) {
NativeWindow.menu.update(this.menuItem, {enabled: true});
this.menuItemEnabled = true;

View File

@ -66,8 +66,11 @@ MOZ_LOCALE_SWITCHER=1
# Enable second screen and casting support for external devices.
MOZ_DEVICES=1
# Enable second screen using native Android libraries
MOZ_NATIVE_DEVICES=1
# Enable second screen using native Android libraries, provided we're
# not resource constrained.
if test -z "$MOZ_ANDROID_RESOURCE_CONSTRAINED"; then
MOZ_NATIVE_DEVICES=1
fi
# Mark as WebGL conformant
MOZ_WEBGL_CONFORMANT=1

View File

@ -30,7 +30,7 @@
@BINPATH@/dictionaries/*
@BINPATH@/hyphenation/*
[assets destdir="assets"]
[assets destdir="assets/@ANDROID_CPU_ARCH@"]
#ifndef MOZ_STATIC_JS
@BINPATH@/@DLL_PREFIX@mozjs@DLL_SUFFIX@
#endif

View File

@ -219,8 +219,8 @@ loadGeckoLibs(const char *apkName)
RefPtr<Zip> zip = ZipCollection::GetZip(apkName);
char *file = new char[strlen(apkName) + sizeof("!/assets/libxul.so")];
sprintf(file, "%s!/assets/libxul.so", apkName);
char *file = new char[strlen(apkName) + sizeof("!/assets/" ANDROID_CPU_ARCH "/libxul.so")];
sprintf(file, "%s!/assets/" ANDROID_CPU_ARCH "/libxul.so", apkName);
xul_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
delete[] file;
@ -278,8 +278,8 @@ loadSQLiteLibs(const char *apkName)
lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
}
char *file = new char[strlen(apkName) + sizeof("!/assets/libmozsqlite3.so")];
sprintf(file, "%s!/assets/libmozsqlite3.so", apkName);
char *file = new char[strlen(apkName) + sizeof("!/assets/" ANDROID_CPU_ARCH "/libmozsqlite3.so")];
sprintf(file, "%s!/assets/" ANDROID_CPU_ARCH "/libmozsqlite3.so", apkName);
sqlite_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
delete [] file;
@ -306,19 +306,19 @@ loadNSSLibs(const char *apkName)
lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
}
char *file = new char[strlen(apkName) + sizeof("!/assets/libnss3.so")];
sprintf(file, "%s!/assets/libnss3.so", apkName);
char *file = new char[strlen(apkName) + sizeof("!/assets/" ANDROID_CPU_ARCH "/libnss3.so")];
sprintf(file, "%s!/assets/" ANDROID_CPU_ARCH "/libnss3.so", apkName);
nss_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
delete [] file;
#ifndef MOZ_FOLD_LIBS
file = new char[strlen(apkName) + sizeof("!/assets/libnspr4.so")];
sprintf(file, "%s!/assets/libnspr4.so", apkName);
file = new char[strlen(apkName) + sizeof("!/assets/" ANDROID_CPU_ARCH "/libnspr4.so")];
sprintf(file, "%s!/assets/" ANDROID_CPU_ARCH "/libnspr4.so", apkName);
nspr_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
delete [] file;
file = new char[strlen(apkName) + sizeof("!/assets/libplc4.so")];
sprintf(file, "%s!/assets/libplc4.so", apkName);
file = new char[strlen(apkName) + sizeof("!/assets/" ANDROID_CPU_ARCH "/libplc4.so")];
sprintf(file, "%s!/assets/" ANDROID_CPU_ARCH "/libplc4.so", apkName);
plc_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
delete [] file;
#endif

View File

@ -21,7 +21,9 @@ FAIL_ON_WARNINGS = True
FINAL_LIBRARY = 'mozglue'
DEFINES['ANDROID_PACKAGE_NAME'] = '"%s"' % CONFIG['ANDROID_PACKAGE_NAME']
for var in ('ANDROID_PACKAGE_NAME',
'ANDROID_CPU_ARCH'):
DEFINES[var] = '"%s"' % CONFIG[var]
if CONFIG['MOZ_FOLD_LIBS']:
DEFINES['MOZ_FOLD_LIBS'] = True

View File

@ -31,12 +31,13 @@
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_UCONTEXT_H
#include <sys/cdefs.h>
#include <signal.h>
#ifdef __BIONIC_UCONTEXT_H
#include <ucontext.h>
#ifdef __BIONIC_HAVE_UCONTEXT_H
# include_next <ucontext.h>
#else
#include <sys/ucontext.h>
# include <sys/ucontext.h>
#endif // __BIONIC_UCONTEXT_H
#ifdef __cplusplus
extern "C" {
@ -51,6 +52,4 @@ int breakpad_getcontext(ucontext_t* ucp);
} // extern "C"
#endif // __cplusplus
#endif // __BIONIC_UCONTEXT_H
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_UCONTEXT_H

View File

@ -266,7 +266,6 @@ this.DebuggerClient = function (aTransport)
this._activeRequests = new Map;
this._eventsEnabled = true;
this.compat = new ProtocolCompatibility(this, []);
this.traits = {};
this.request = this.request.bind(this);
@ -858,81 +857,73 @@ DebuggerClient.prototype = {
*
* @param aPacket object
* The incoming packet.
* @param aIgnoreCompatibility boolean
* Set true to not pass the packet through the compatibility layer.
*/
onPacket: function (aPacket, aIgnoreCompatibility=false) {
let packet = aIgnoreCompatibility
? aPacket
: this.compat.onPacket(aPacket);
onPacket: function (aPacket) {
if (!aPacket.from) {
DevToolsUtils.reportException(
"onPacket",
new Error("Server did not specify an actor, dropping packet: " +
JSON.stringify(aPacket)));
return;
}
resolve(packet).then(aPacket => {
if (!aPacket.from) {
DevToolsUtils.reportException(
"onPacket",
new Error("Server did not specify an actor, dropping packet: " +
JSON.stringify(aPacket)));
// If we have a registered Front for this actor, let it handle the packet
// and skip all the rest of this unpleasantness.
let front = this.getActor(aPacket.from);
if (front) {
front.onPacket(aPacket);
return;
}
if (this._clients.has(aPacket.from) && aPacket.type) {
let client = this._clients.get(aPacket.from);
let type = aPacket.type;
if (client.events.indexOf(type) != -1) {
client.emit(type, aPacket);
// we ignore the rest, as the client is expected to handle this packet.
return;
}
}
// If we have a registered Front for this actor, let it handle the packet
// and skip all the rest of this unpleasantness.
let front = this.getActor(aPacket.from);
if (front) {
front.onPacket(aPacket);
return;
}
let activeRequest;
// See if we have a handler function waiting for a reply from this
// actor. (Don't count unsolicited notifications or pauses as
// replies.)
if (this._activeRequests.has(aPacket.from) &&
!(aPacket.type in UnsolicitedNotifications) &&
!(aPacket.type == ThreadStateTypes.paused &&
aPacket.why.type in UnsolicitedPauses)) {
activeRequest = this._activeRequests.get(aPacket.from);
this._activeRequests.delete(aPacket.from);
}
if (this._clients.has(aPacket.from) && aPacket.type) {
let client = this._clients.get(aPacket.from);
let type = aPacket.type;
if (client.events.indexOf(type) != -1) {
client.emit(type, aPacket);
// we ignore the rest, as the client is expected to handle this packet.
return;
}
}
// Packets that indicate thread state changes get special treatment.
if (aPacket.type in ThreadStateTypes &&
this._clients.has(aPacket.from) &&
typeof this._clients.get(aPacket.from)._onThreadState == "function") {
this._clients.get(aPacket.from)._onThreadState(aPacket);
}
// On navigation the server resumes, so the client must resume as well.
// We achieve that by generating a fake resumption packet that triggers
// the client's thread state change listeners.
if (aPacket.type == UnsolicitedNotifications.tabNavigated &&
this._clients.has(aPacket.from) &&
this._clients.get(aPacket.from).thread) {
let thread = this._clients.get(aPacket.from).thread;
let resumption = { from: thread._actor, type: "resumed" };
thread._onThreadState(resumption);
}
// Only try to notify listeners on events, not responses to requests
// that lack a packet type.
if (aPacket.type) {
this.emit(aPacket.type, aPacket);
}
let activeRequest;
// See if we have a handler function waiting for a reply from this
// actor. (Don't count unsolicited notifications or pauses as
// replies.)
if (this._activeRequests.has(aPacket.from) &&
!(aPacket.type in UnsolicitedNotifications) &&
!(aPacket.type == ThreadStateTypes.paused &&
aPacket.why.type in UnsolicitedPauses)) {
activeRequest = this._activeRequests.get(aPacket.from);
this._activeRequests.delete(aPacket.from);
}
if (activeRequest) {
activeRequest.emit("json-reply", aPacket);
}
// Packets that indicate thread state changes get special treatment.
if (aPacket.type in ThreadStateTypes &&
this._clients.has(aPacket.from) &&
typeof this._clients.get(aPacket.from)._onThreadState == "function") {
this._clients.get(aPacket.from)._onThreadState(aPacket);
}
// On navigation the server resumes, so the client must resume as well.
// We achieve that by generating a fake resumption packet that triggers
// the client's thread state change listeners.
if (aPacket.type == UnsolicitedNotifications.tabNavigated &&
this._clients.has(aPacket.from) &&
this._clients.get(aPacket.from).thread) {
let thread = this._clients.get(aPacket.from).thread;
let resumption = { from: thread._actor, type: "resumed" };
thread._onThreadState(resumption);
}
// Only try to notify listeners on events, not responses to requests
// that lack a packet type.
if (aPacket.type) {
this.emit(aPacket.type, aPacket);
}
if (activeRequest) {
activeRequest.emit("json-reply", aPacket);
}
this._sendRequests();
}, ex => DevToolsUtils.reportException("onPacket handler", ex));
this._sendRequests();
},
/**
@ -1096,156 +1087,6 @@ Request.prototype = {
};
// Constants returned by `FeatureCompatibilityShim.onPacketTest`.
const SUPPORTED = 1;
const NOT_SUPPORTED = 2;
const SKIP = 3;
/**
* This object provides an abstraction layer over all of our backwards
* compatibility, feature detection, and shimming with regards to the remote
* debugging prototcol.
*
* @param aFeatures Array
* An array of FeatureCompatibilityShim objects
*/
function ProtocolCompatibility(aClient, aFeatures) {
this._client = aClient;
this._featuresWithUnknownSupport = new Set(aFeatures);
this._featuresWithoutSupport = new Set();
this._featureDeferreds = Object.create(null)
for (let f of aFeatures) {
this._featureDeferreds[f.name] = defer();
}
}
ProtocolCompatibility.prototype = {
/**
* Returns a promise that resolves to true if the RDP supports the feature,
* and is rejected otherwise.
*
* @param aFeatureName String
* The name of the feature we are testing.
*/
supportsFeature: function (aFeatureName) {
return this._featureDeferreds[aFeatureName].promise;
},
/**
* Force a feature to be considered unsupported.
*
* @param aFeatureName String
* The name of the feature we are testing.
*/
rejectFeature: function (aFeatureName) {
this._featureDeferreds[aFeatureName].reject(false);
},
/**
* Called for each packet received over the RDP from the server. Tests for
* protocol features and shims packets to support needed features.
*
* @param aPacket Object
* Packet received over the RDP from the server.
*/
onPacket: function (aPacket) {
this._detectFeatures(aPacket);
return this._shimPacket(aPacket);
},
/**
* For each of the features we don't know whether the server supports or not,
* attempt to detect support based on the packet we just received.
*/
_detectFeatures: function (aPacket) {
for (let feature of this._featuresWithUnknownSupport) {
try {
switch (feature.onPacketTest(aPacket)) {
case SKIP:
break;
case SUPPORTED:
this._featuresWithUnknownSupport.delete(feature);
this._featureDeferreds[feature.name].resolve(true);
break;
case NOT_SUPPORTED:
this._featuresWithUnknownSupport.delete(feature);
this._featuresWithoutSupport.add(feature);
this.rejectFeature(feature.name);
break;
default:
DevToolsUtils.reportException(
"PC__detectFeatures",
new Error("Bad return value from `onPacketTest` for feature '"
+ feature.name + "'"));
}
} catch (ex) {
DevToolsUtils.reportException("PC__detectFeatures", ex);
}
}
},
/**
* Go through each of the features that we know are unsupported by the current
* server and attempt to shim support.
*/
_shimPacket: function (aPacket) {
let extraPackets = [];
let loop = (aFeatures, aPacket) => {
if (aFeatures.length === 0) {
for (let packet of extraPackets) {
this._client.onPacket(packet, true);
}
return aPacket;
} else {
let replacePacket = function (aNewPacket) {
return aNewPacket;
};
let extraPacket = function (aExtraPacket) {
extraPackets.push(aExtraPacket);
return aPacket;
};
let keepPacket = function () {
return aPacket;
};
let newPacket = aFeatures[0].translatePacket(aPacket,
replacePacket,
extraPacket,
keepPacket);
return resolve(newPacket).then(loop.bind(null, aFeatures.slice(1)));
}
};
return loop([f for (f of this._featuresWithoutSupport)],
aPacket);
}
};
/**
* Interface defining what methods a feature compatibility shim should have.
*/
const FeatureCompatibilityShim = {
// The name of the feature
name: null,
/**
* Takes a packet and returns boolean (or promise of boolean) indicating
* whether the server supports the RDP feature we are possibly shimming.
*/
onPacketTest: function (aPacket) {
throw new Error("Not yet implemented");
},
/**
* Takes a packet actually sent from the server and decides whether to replace
* it with a new packet, create an extra packet, or keep it.
*/
translatePacket: function (aPacket, aReplacePacket, aExtraPacket, aKeepPacket) {
throw new Error("Not yet implemented");
}
};
/**
* Creates a tab client for the remote debugging protocol server. This client
* is a front to the tab actor created in the server side, hiding the protocol
@ -1503,7 +1344,6 @@ ThreadClient.prototype = {
_actor: null,
get actor() { return this._actor; },
get compat() { return this.client.compat; },
get _transport() { return this.client._transport; },
_assertPaused: function (aCommand) {

View File

@ -272,7 +272,7 @@ PendingErrors.addObserver(function(details) {
}
error.init(
/*message*/ generalDescription +
"Date: " + details.date + "\nFull Message: " + details.message,
"Date: " + details.date + "\nFull Message: " + message,
/*sourceName*/ details.fileName,
/*sourceLine*/ details.lineNumber?("" + details.lineNumber):0,
/*lineNumber*/ details.lineNumber || 0,

View File

@ -380,14 +380,15 @@ DIST_FILES += libomxplugin.so libomxplugingb.so libomxplugingb235.so \
endif
SO_LIBRARIES := $(filter %.so,$(DIST_FILES))
# These libraries are placed in the assets/ directory by packager.py.
ASSET_SO_LIBRARIES := $(addprefix assets/,$(filter-out libmozglue.so $(MOZ_CHILD_PROCESS_NAME),$(SO_LIBRARIES)))
# These libraries are placed in the assets/$(ANDROID_CPU_ARCH) directory by packager.py.
ASSET_SO_LIBRARIES := $(addprefix assets/$(ANDROID_CPU_ARCH)/,$(filter-out libmozglue.so $(MOZ_CHILD_PROCESS_NAME),$(SO_LIBRARIES)))
DIST_FILES := $(filter-out $(SO_LIBRARIES),$(DIST_FILES))
NON_DIST_FILES += libmozglue.so $(MOZ_CHILD_PROCESS_NAME) $(ASSET_SO_LIBRARIES)
ifdef MOZ_ENABLE_SZIP
# These libraries are szipped in-place in the assets/ directory.
# These libraries are szipped in-place in the
# assets/$(ANDROID_CPU_ARCH) directory.
SZIP_LIBRARIES := $(ASSET_SO_LIBRARIES)
endif