mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge fx-team to mozilla-central a=merge
This commit is contained in:
commit
fff6a458cd
@ -126,6 +126,15 @@ function URL(url, base) {
|
||||
}
|
||||
}
|
||||
|
||||
let fileName = "/";
|
||||
try {
|
||||
fileName = uri.QueryInterface(Ci.nsIURL).fileName;
|
||||
} catch (e) {
|
||||
if (e.result != Cr.NS_NOINTERFACE) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
let uriData = [uri.path, uri.path.length, {}, {}, {}, {}, {}, {}];
|
||||
URLParser.parsePath.apply(URLParser, uriData);
|
||||
let [{ value: filepathPos }, { value: filepathLen },
|
||||
@ -137,6 +146,7 @@ function URL(url, base) {
|
||||
let search = uri.path.substr(queryPos, queryLen);
|
||||
search = search ? "?" + search : "";
|
||||
|
||||
this.__defineGetter__("fileName", () => fileName);
|
||||
this.__defineGetter__("scheme", () => uri.scheme);
|
||||
this.__defineGetter__("userPass", () => userPass);
|
||||
this.__defineGetter__("host", () => host);
|
||||
|
@ -20,8 +20,6 @@ const { decode } = require('sdk/base64');
|
||||
const httpd = require('./lib/httpd');
|
||||
const port = 8099;
|
||||
|
||||
const defaultLocation = '{\'scheme\':\'about\',\'userPass\':null,\'host\':null,\'hostname\':null,\'port\':null,\'path\':\'addons\',\'pathname\':\'addons\',\'hash\':\'\',\'href\':\'about:addons\',\'origin\':\'about:\',\'protocol\':\'about:\',\'search\':\'\'}'.replace(/'/g, '"');
|
||||
|
||||
exports.testResolve = function(assert) {
|
||||
assert.equal(URL('bar', 'http://www.foo.com/').toString(),
|
||||
'http://www.foo.com/bar');
|
||||
@ -65,6 +63,7 @@ exports.testParseHttp = function(assert) {
|
||||
assert.equal(info.href, aUrl);
|
||||
assert.equal(info.hash, '#myhash');
|
||||
assert.equal(info.search, '?locale=en-US&otherArg=%20x%20');
|
||||
assert.equal(info.fileName, 'bar');
|
||||
};
|
||||
|
||||
exports.testParseHttpSearchAndHash = function (assert) {
|
||||
@ -109,6 +108,7 @@ exports.testParseChrome = function(assert) {
|
||||
assert.equal(info.port, null);
|
||||
assert.equal(info.userPass, null);
|
||||
assert.equal(info.path, '/content/blah');
|
||||
assert.equal(info.fileName, 'blah');
|
||||
};
|
||||
|
||||
exports.testParseAbout = function(assert) {
|
||||
@ -127,6 +127,7 @@ exports.testParseFTP = function(assert) {
|
||||
assert.equal(info.port, null);
|
||||
assert.equal(info.userPass, null);
|
||||
assert.equal(info.path, '/foo');
|
||||
assert.equal(info.fileName, 'foo');
|
||||
};
|
||||
|
||||
exports.testParseFTPWithUserPass = function(assert) {
|
||||
@ -216,7 +217,7 @@ exports.testStringInterface = function(assert) {
|
||||
|
||||
// make sure the standard URL properties are enumerable and not the String interface bits
|
||||
assert.equal(Object.keys(a),
|
||||
'scheme,userPass,host,hostname,port,path,pathname,hash,href,origin,protocol,search',
|
||||
'fileName,scheme,userPass,host,hostname,port,path,pathname,hash,href,origin,protocol,search',
|
||||
'enumerable key list check for URL.');
|
||||
assert.equal(
|
||||
JSON.stringify(a),
|
||||
@ -392,6 +393,20 @@ exports.testLocalURLwithInvalidURL = function(assert) {
|
||||
});
|
||||
}
|
||||
|
||||
exports.testFileName = function(assert) {
|
||||
let urls = [
|
||||
['https://foo/bar.js', 'bar.js'],
|
||||
['app://myfxosapp/file.js', 'file.js'],
|
||||
['http://localhost:8888/file.js', 'file.js'],
|
||||
['http://foo/bar.js#hash', 'bar.js'],
|
||||
['http://foo/bar.js?q=go&query=yeah', 'bar.js'],
|
||||
['chrome://browser/content/content.js', 'content.js'],
|
||||
['resource://gre/foo.js', 'foo.js'],
|
||||
];
|
||||
|
||||
urls.forEach(([url, fileName]) => assert.equal(URL(url).fileName, fileName, 'file names are equal'));
|
||||
};
|
||||
|
||||
function validURIs() {
|
||||
return [
|
||||
'http://foo.com/blah_blah',
|
||||
|
@ -186,12 +186,11 @@ body {
|
||||
}
|
||||
|
||||
.rooms > h1 {
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
font-size: 1rem;
|
||||
padding: .5rem 0;
|
||||
height: 3rem;
|
||||
line-height: 3rem;
|
||||
font-size: 1.1rem;
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
@ -205,7 +204,7 @@ body {
|
||||
border-radius: 5px;
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
margin: 1rem;
|
||||
margin: 1.5rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
@ -252,8 +251,8 @@ body {
|
||||
line-height: 2.4rem;
|
||||
color: #000;
|
||||
/* See .room-entry-context-item for the margin/size reductions.
|
||||
* An extra 40px to make space for the call button and chevron. */
|
||||
width: calc(100% - 1rem - 56px);
|
||||
* An extra 16px to make space for the edit button. */
|
||||
width: calc(100% - 1rem - 32px);
|
||||
}
|
||||
|
||||
.room-list > .room-entry.room-active:not(.room-opened) > h2 {
|
||||
@ -331,27 +330,9 @@ body {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.room-list > .room-entry > h2:before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
background-image: url("../shared/img/icons-14x14.svg#hello");
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
-moz-margin-end: 1rem;
|
||||
margin-bottom: -3px;
|
||||
}
|
||||
|
||||
.room-list > .room-entry.room-active > h2:before {
|
||||
background-image: url("../shared/img/icons-14x14.svg#hello-active");
|
||||
}
|
||||
|
||||
/* Room entry context button (call button + chevron) */
|
||||
/* Room entry context button (edit button) */
|
||||
.room-entry-context-actions {
|
||||
display: none;
|
||||
border-radius: 30px;
|
||||
background: #56b397;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
@ -359,41 +340,17 @@ body {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.room-entry:hover .room-entry-context-actions:hover {
|
||||
background: #50e3c2;
|
||||
}
|
||||
|
||||
/* Room entry call button */
|
||||
.room-entry-call-btn {
|
||||
border-top-left-radius: 30px;
|
||||
border-bottom-left-radius: 30px;
|
||||
background-color: transparent;
|
||||
background-image: url("../shared/img/icons-14x14.svg#video-white");
|
||||
background-position: right center;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .room-entry-call-btn {
|
||||
background-position: left center;
|
||||
}
|
||||
|
||||
/* Room entry context menu */
|
||||
.room-entry-context-menu-chevron {
|
||||
display: inline-block;
|
||||
border-top-right-radius: 30px;
|
||||
border-bottom-right-radius: 30px;
|
||||
background-image: url("../shared/img/icons-10x10.svg#dropdown-white");
|
||||
/* Room entry edit button */
|
||||
.room-entry-context-edit-btn {
|
||||
background-image: url("../shared/img/icons-10x10.svg#edit-darkgrey");
|
||||
background-position: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Common styles for chevron and call button. */
|
||||
.room-entry-context-menu-chevron,
|
||||
.room-entry-call-btn {
|
||||
width: 30px;
|
||||
height: 24px;
|
||||
background-size: 12px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 12px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 24px;
|
||||
vertical-align: middle;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .room-entry-context-actions > .dropdown-menu {
|
||||
@ -410,16 +367,11 @@ html[dir="rtl"] .room-entry-context-actions > .dropdown-menu {
|
||||
/* Keep ".room-list > .room-entry > h2" in sync with these. */
|
||||
.room-entry-context-item {
|
||||
display: inline-block;
|
||||
-moz-margin-end: 1rem;
|
||||
vertical-align: middle;
|
||||
-moz-margin-start: 1rem;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.room-entry:not(.room-opened):hover .room-entry-context-item {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.room-entry-context-item > a > img {
|
||||
.room-entry-context-item > img {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
|
@ -340,23 +340,39 @@ loop.panel = (function(_, mozL10n) {
|
||||
handleClick: function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.props.mozLoop.openURL(event.currentTarget.href);
|
||||
this.closeWindow();
|
||||
if (event.currentTarget.href) {
|
||||
this.props.mozLoop.openURL(event.currentTarget.href);
|
||||
this.closeWindow();
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var roomUrl = this.props.roomUrls && this.props.roomUrls[0];
|
||||
if (!roomUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_renderDefaultIcon: function() {
|
||||
return (
|
||||
React.createElement("div", {className: "room-entry-context-item"},
|
||||
React.createElement("a", {href: roomUrl.location, onClick: this.handleClick, title: roomUrl.description},
|
||||
React.createElement("img", {src: "loop/shared/img/icons-16x16.svg#globe"})
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
_renderIcon: function(roomUrl) {
|
||||
return (
|
||||
React.createElement("div", {className: "room-entry-context-item"},
|
||||
React.createElement("a", {href: roomUrl.location,
|
||||
onClick: this.handleClick,
|
||||
title: roomUrl.description},
|
||||
React.createElement("img", {src: roomUrl.thumbnail || "loop/shared/img/icons-16x16.svg#globe"})
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var roomUrl = this.props.roomUrls && this.props.roomUrls[0];
|
||||
if (roomUrl && roomUrl.location) {
|
||||
return this._renderIcon(roomUrl);
|
||||
} else {
|
||||
return this._renderDefaultIcon();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -398,7 +414,7 @@ loop.panel = (function(_, mozL10n) {
|
||||
this.closeWindow();
|
||||
},
|
||||
|
||||
handleContextChevronClick: function(e) {
|
||||
handleClick: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
@ -435,23 +451,19 @@ loop.panel = (function(_, mozL10n) {
|
||||
onClick: this.props.isOpenedRoom ? null : this.handleClickEntry,
|
||||
onMouseLeave: this.props.isOpenedRoom ? null : this._handleMouseOut,
|
||||
ref: "roomEntry"},
|
||||
React.createElement("h2", null,
|
||||
roomTitle
|
||||
),
|
||||
React.createElement(RoomEntryContextItem, {
|
||||
mozLoop: this.props.mozLoop,
|
||||
roomUrls: this.props.room.decryptedContext.urls}),
|
||||
React.createElement("h2", null, roomTitle),
|
||||
this.props.isOpenedRoom ? null :
|
||||
React.createElement(RoomEntryContextButtons, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
eventPosY: this.state.eventPosY,
|
||||
handleClickEntry: this.handleClickEntry,
|
||||
handleContextChevronClick: this.handleContextChevronClick,
|
||||
handleClick: this.handleClick,
|
||||
ref: "contextActions",
|
||||
room: this.props.room,
|
||||
showMenu: this.state.showMenu,
|
||||
toggleDropdownMenu: this.toggleDropdownMenu})
|
||||
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -459,16 +471,14 @@ loop.panel = (function(_, mozL10n) {
|
||||
|
||||
/**
|
||||
* Buttons corresponding to each conversation entry.
|
||||
* This component renders the video icon call button and chevron button for
|
||||
* displaying contextual dropdown menu for conversation entries.
|
||||
* It also holds the dropdown menu.
|
||||
* This component renders the edit button for displaying contextual dropdown
|
||||
* menu for conversation entries. It also holds the dropdown menu.
|
||||
*/
|
||||
var RoomEntryContextButtons = React.createClass({displayName: "RoomEntryContextButtons",
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.object.isRequired,
|
||||
eventPosY: React.PropTypes.number.isRequired,
|
||||
handleClickEntry: React.PropTypes.func.isRequired,
|
||||
handleContextChevronClick: React.PropTypes.func.isRequired,
|
||||
handleClick: React.PropTypes.func.isRequired,
|
||||
room: React.PropTypes.object.isRequired,
|
||||
showMenu: React.PropTypes.bool.isRequired,
|
||||
toggleDropdownMenu: React.PropTypes.func.isRequired
|
||||
@ -514,13 +524,9 @@ loop.panel = (function(_, mozL10n) {
|
||||
render: function() {
|
||||
return (
|
||||
React.createElement("div", {className: "room-entry-context-actions"},
|
||||
React.createElement("button", {
|
||||
className: "btn room-entry-call-btn",
|
||||
onClick: this.props.handleClickEntry,
|
||||
ref: "callButton"}),
|
||||
React.createElement("div", {
|
||||
className: "room-entry-context-menu-chevron dropdown-menu-button",
|
||||
onClick: this.props.handleContextChevronClick,
|
||||
className: "room-entry-context-edit-btn dropdown-menu-button",
|
||||
onClick: this.props.handleClick,
|
||||
ref: "menu-button"}),
|
||||
this.props.showMenu ?
|
||||
React.createElement(ConversationDropdown, {
|
||||
@ -563,7 +569,7 @@ loop.panel = (function(_, mozL10n) {
|
||||
// Get the parent element and make sure the menu does not overlow its
|
||||
// container.
|
||||
var listNode = loop.shared.utils.findParentNode(this.getDOMNode(),
|
||||
".rooms");
|
||||
"rooms");
|
||||
var listNodeRect = listNode.getBoundingClientRect();
|
||||
|
||||
// Click offset to not display the menu right next to the area clicked.
|
||||
|
@ -340,23 +340,39 @@ loop.panel = (function(_, mozL10n) {
|
||||
handleClick: function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.props.mozLoop.openURL(event.currentTarget.href);
|
||||
this.closeWindow();
|
||||
if (event.currentTarget.href) {
|
||||
this.props.mozLoop.openURL(event.currentTarget.href);
|
||||
this.closeWindow();
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var roomUrl = this.props.roomUrls && this.props.roomUrls[0];
|
||||
if (!roomUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_renderDefaultIcon: function() {
|
||||
return (
|
||||
<div className="room-entry-context-item">
|
||||
<a href={roomUrl.location} onClick={this.handleClick} title={roomUrl.description}>
|
||||
<img src="loop/shared/img/icons-16x16.svg#globe" />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
_renderIcon: function(roomUrl) {
|
||||
return (
|
||||
<div className="room-entry-context-item">
|
||||
<a href={roomUrl.location}
|
||||
onClick={this.handleClick}
|
||||
title={roomUrl.description}>
|
||||
<img src={roomUrl.thumbnail || "loop/shared/img/icons-16x16.svg#globe"} />
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var roomUrl = this.props.roomUrls && this.props.roomUrls[0];
|
||||
if (roomUrl && roomUrl.location) {
|
||||
return this._renderIcon(roomUrl);
|
||||
} else {
|
||||
return this._renderDefaultIcon();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -398,7 +414,7 @@ loop.panel = (function(_, mozL10n) {
|
||||
this.closeWindow();
|
||||
},
|
||||
|
||||
handleContextChevronClick: function(e) {
|
||||
handleClick: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
@ -435,23 +451,19 @@ loop.panel = (function(_, mozL10n) {
|
||||
onClick={this.props.isOpenedRoom ? null : this.handleClickEntry}
|
||||
onMouseLeave={this.props.isOpenedRoom ? null : this._handleMouseOut}
|
||||
ref="roomEntry">
|
||||
<h2>
|
||||
{roomTitle}
|
||||
</h2>
|
||||
<RoomEntryContextItem
|
||||
mozLoop={this.props.mozLoop}
|
||||
roomUrls={this.props.room.decryptedContext.urls} />
|
||||
<h2>{roomTitle}</h2>
|
||||
{this.props.isOpenedRoom ? null :
|
||||
<RoomEntryContextButtons
|
||||
dispatcher={this.props.dispatcher}
|
||||
eventPosY={this.state.eventPosY}
|
||||
handleClickEntry={this.handleClickEntry}
|
||||
handleContextChevronClick={this.handleContextChevronClick}
|
||||
handleClick={this.handleClick}
|
||||
ref="contextActions"
|
||||
room={this.props.room}
|
||||
showMenu={this.state.showMenu}
|
||||
toggleDropdownMenu={this.toggleDropdownMenu} />
|
||||
}
|
||||
toggleDropdownMenu={this.toggleDropdownMenu} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -459,16 +471,14 @@ loop.panel = (function(_, mozL10n) {
|
||||
|
||||
/**
|
||||
* Buttons corresponding to each conversation entry.
|
||||
* This component renders the video icon call button and chevron button for
|
||||
* displaying contextual dropdown menu for conversation entries.
|
||||
* It also holds the dropdown menu.
|
||||
* This component renders the edit button for displaying contextual dropdown
|
||||
* menu for conversation entries. It also holds the dropdown menu.
|
||||
*/
|
||||
var RoomEntryContextButtons = React.createClass({
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.object.isRequired,
|
||||
eventPosY: React.PropTypes.number.isRequired,
|
||||
handleClickEntry: React.PropTypes.func.isRequired,
|
||||
handleContextChevronClick: React.PropTypes.func.isRequired,
|
||||
handleClick: React.PropTypes.func.isRequired,
|
||||
room: React.PropTypes.object.isRequired,
|
||||
showMenu: React.PropTypes.bool.isRequired,
|
||||
toggleDropdownMenu: React.PropTypes.func.isRequired
|
||||
@ -514,13 +524,9 @@ loop.panel = (function(_, mozL10n) {
|
||||
render: function() {
|
||||
return (
|
||||
<div className="room-entry-context-actions">
|
||||
<button
|
||||
className="btn room-entry-call-btn"
|
||||
onClick={this.props.handleClickEntry}
|
||||
ref="callButton" />
|
||||
<div
|
||||
className="room-entry-context-menu-chevron dropdown-menu-button"
|
||||
onClick={this.props.handleContextChevronClick}
|
||||
className="room-entry-context-edit-btn dropdown-menu-button"
|
||||
onClick={this.props.handleClick}
|
||||
ref="menu-button" />
|
||||
{this.props.showMenu ?
|
||||
<ConversationDropdown
|
||||
@ -563,7 +569,7 @@ loop.panel = (function(_, mozL10n) {
|
||||
// Get the parent element and make sure the menu does not overlow its
|
||||
// container.
|
||||
var listNode = loop.shared.utils.findParentNode(this.getDOMNode(),
|
||||
".rooms");
|
||||
"rooms");
|
||||
var listNodeRect = listNode.getBoundingClientRect();
|
||||
|
||||
// Click offset to not display the menu right next to the area clicked.
|
||||
|
@ -22,6 +22,9 @@
|
||||
use[id$="-disabled"] {
|
||||
fill: rgba(255,255,255,0.4);
|
||||
}
|
||||
use[id$="-darkgrey"] {
|
||||
fill: #565656;
|
||||
}
|
||||
</style>
|
||||
<defs>
|
||||
<polygon id="close-shape" points="10,1.717 8.336,0.049 5.024,3.369 1.663,0 0,1.668 3.36,5.037 0.098,8.307 1.762,9.975 5.025,6.705 8.311,10 9.975,8.332 6.688,5.037"/>
|
||||
@ -43,6 +46,7 @@
|
||||
<use id="edit-active" xlink:href="#edit-shape"/>
|
||||
<use id="edit-disabled" xlink:href="#edit-shape"/>
|
||||
<use id="edit-white" xlink:href="#edit-shape"/>
|
||||
<use id="edit-darkgrey" xlink:href="#edit-shape"/>
|
||||
<use id="expand" xlink:href="#expand-shape"/>
|
||||
<use id="expand-active" xlink:href="#expand-shape"/>
|
||||
<use id="expand-disabled" xlink:href="#expand-shape"/>
|
||||
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 6.0 KiB |
@ -593,7 +593,7 @@ describe("loop.panel", function() {
|
||||
React.createElement(loop.panel.RoomEntry, props));
|
||||
}
|
||||
|
||||
describe("handleContextChevronClick", function() {
|
||||
describe("handleClick", function() {
|
||||
var view;
|
||||
|
||||
beforeEach(function() {
|
||||
@ -613,15 +613,15 @@ describe("loop.panel", function() {
|
||||
expect(view.refs.contextActions.state.showMenu).to.eql(false);
|
||||
});
|
||||
|
||||
it("should set eventPosY when handleContextChevronClick is called", function() {
|
||||
view.handleContextChevronClick(fakeEvent);
|
||||
it("should set eventPosY when handleClick is called", function() {
|
||||
view.handleClick(fakeEvent);
|
||||
|
||||
expect(view.state.eventPosY).to.eql(fakeEvent.pageY);
|
||||
});
|
||||
|
||||
it("toggle state.showMenu when handleContextChevronClick is called", function() {
|
||||
it("toggle state.showMenu when handleClick is called", function() {
|
||||
var prevState = view.state.showMenu;
|
||||
view.handleContextChevronClick(fakeEvent);
|
||||
view.handleClick(fakeEvent);
|
||||
|
||||
expect(view.state.showMenu).to.eql(!prevState);
|
||||
});
|
||||
@ -702,10 +702,10 @@ describe("loop.panel", function() {
|
||||
});
|
||||
}
|
||||
|
||||
it("should not display a context indicator if the room doesn't have any", function() {
|
||||
it("should display a default context indicator if the room doesn't have any", function() {
|
||||
roomEntry = mountEntryForContext();
|
||||
|
||||
expect(roomEntry.getDOMNode().querySelector(".room-entry-context-item")).eql(null);
|
||||
expect(roomEntry.getDOMNode().querySelector(".room-entry-context-item")).not.eql(null);
|
||||
});
|
||||
|
||||
it("should a context indicator if the room specifies context", function() {
|
||||
@ -1129,11 +1129,10 @@ describe("loop.panel", function() {
|
||||
var props = _.extend({
|
||||
dispatcher: dispatcher,
|
||||
eventPosY: 0,
|
||||
handleClickEntry: sandbox.stub(),
|
||||
showMenu: false,
|
||||
room: roomData,
|
||||
toggleDropdownMenu: sandbox.stub(),
|
||||
handleContextChevronClick: sandbox.stub()
|
||||
handleClick: sandbox.stub()
|
||||
}, extraProps);
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.panel.RoomEntryContextButtons, props));
|
||||
@ -1192,11 +1191,5 @@ describe("loop.panel", function() {
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.DeleteRoom({ roomToken: roomData.roomToken }));
|
||||
});
|
||||
|
||||
it("should trigger handleClickEntry when button is clicked", function() {
|
||||
TestUtils.Simulate.click(view.refs.callButton.getDOMNode());
|
||||
|
||||
sinon.assert.calledOnce(view.props.handleClickEntry);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2,8 +2,7 @@
|
||||
# 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/.
|
||||
|
||||
mobile-tests := mobile/android/tests/browser/robocop
|
||||
TESTPATH := $(topsrcdir)/$(mobile-tests)
|
||||
TESTPATH := $(topsrcdir)/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests
|
||||
|
||||
ANDROID_EXTRA_JARS += \
|
||||
$(srcdir)/robotium-solo-4.3.1.jar \
|
||||
@ -29,7 +28,7 @@ _JAVA_HARNESS := \
|
||||
StructuredLogger.java \
|
||||
$(NULL)
|
||||
|
||||
java-harness := $(addprefix $(srcdir)/,$(_JAVA_HARNESS))
|
||||
java-harness := $(addprefix $(topsrcdir)/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/,$(_JAVA_HARNESS))
|
||||
java-tests := \
|
||||
$(wildcard $(TESTPATH)/*.java) \
|
||||
$(wildcard $(TESTPATH)/components/*.java) \
|
||||
|
@ -278,21 +278,17 @@ Tools.performance = {
|
||||
Tools.memory = {
|
||||
id: "memory",
|
||||
ordinal: 8,
|
||||
icon: "chrome://devtools/skin/themes/images/tool-styleeditor.svg",
|
||||
icon: "chrome://devtools/skin/themes/images/tool-memory.svg",
|
||||
invertIconForLightTheme: true,
|
||||
highlightedicon: "chrome://devtools/skin/themes/images/tool-memory-active.svg",
|
||||
url: "chrome://devtools/content/memory/memory.xhtml",
|
||||
visibilityswitch: "devtools.memory.enabled",
|
||||
label: "Memory",
|
||||
panelLabel: "Memory Panel",
|
||||
tooltip: "Memory (keyboardshortcut)",
|
||||
hiddenInOptions: true,
|
||||
|
||||
isTargetSupported: function (target) {
|
||||
// TODO 1201907
|
||||
// Once Fx44 lands, we should add a root trait `heapSnapshots`
|
||||
// to indicate that the memory actor can handle this.
|
||||
// Shouldn't make this change until Fx44, however.
|
||||
return true; // target.getTrait("heapSnapshots");
|
||||
return target.getTrait("heapSnapshots");
|
||||
},
|
||||
|
||||
build: function (frame, target) {
|
||||
|
@ -302,6 +302,8 @@ devtools.jar:
|
||||
skin/themes/images/tool-network.svg (themes/images/tool-network.svg)
|
||||
skin/themes/images/tool-scratchpad.svg (themes/images/tool-scratchpad.svg)
|
||||
skin/themes/images/tool-webaudio.svg (themes/images/tool-webaudio.svg)
|
||||
skin/themes/images/tool-memory.svg (themes/images/tool-memory.svg)
|
||||
skin/themes/images/tool-memory-active.svg (themes/images/tool-memory-active.svg)
|
||||
skin/themes/images/close.png (themes/images/close.png)
|
||||
skin/themes/images/close@2x.png (themes/images/close@2x.png)
|
||||
skin/themes/images/vview-delete.png (themes/images/vview-delete.png)
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
|
||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||
const { breakdowns } = require("./constants");
|
||||
const { toggleRecordingAllocationStacks } = require("./actions/allocations");
|
||||
const { setBreakdownAndRefresh } = require("./actions/breakdown");
|
||||
const { toggleInvertedAndRefresh } = require("./actions/inverted");
|
||||
@ -20,15 +21,24 @@ const App = createClass({
|
||||
|
||||
propTypes: appModel,
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
breakdown: breakdowns.coarseType.breakdown,
|
||||
inverted: false,
|
||||
};
|
||||
},
|
||||
|
||||
childContextTypes: {
|
||||
front: PropTypes.any,
|
||||
heapWorker: PropTypes.any,
|
||||
toolbox: PropTypes.any,
|
||||
},
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
front: this.props.front,
|
||||
heapWorker: this.props.heapWorker,
|
||||
toolbox: this.props.toolbox,
|
||||
}
|
||||
},
|
||||
|
||||
@ -40,13 +50,14 @@ const App = createClass({
|
||||
heapWorker,
|
||||
breakdown,
|
||||
allocations,
|
||||
inverted
|
||||
inverted,
|
||||
toolbox,
|
||||
} = this.props;
|
||||
|
||||
let selectedSnapshot = snapshots.find(s => s.selected);
|
||||
|
||||
return (
|
||||
dom.div({ id: "memory-tool" }, [
|
||||
dom.div({ id: "memory-tool" },
|
||||
|
||||
Toolbar({
|
||||
breakdowns: getBreakdownDisplayData(),
|
||||
@ -61,7 +72,7 @@ const App = createClass({
|
||||
dispatch(toggleInvertedAndRefresh(heapWorker))
|
||||
}),
|
||||
|
||||
dom.div({ id: "memory-tool-container" }, [
|
||||
dom.div({ id: "memory-tool-container" },
|
||||
List({
|
||||
itemComponent: SnapshotListItem,
|
||||
items: snapshots,
|
||||
@ -71,9 +82,10 @@ const App = createClass({
|
||||
HeapView({
|
||||
snapshot: selectedSnapshot,
|
||||
onSnapshotClick: () => dispatch(takeSnapshotAndCensus(front, heapWorker)),
|
||||
}),
|
||||
])
|
||||
])
|
||||
toolbox
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
40
devtools/client/memory/components/frame.js
Normal file
40
devtools/client/memory/components/frame.js
Normal file
@ -0,0 +1,40 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
|
||||
const { URL } = require("sdk/url");
|
||||
|
||||
const Frame = module.exports = createClass({
|
||||
displayName: "frame-view",
|
||||
|
||||
propTypes: {
|
||||
frame: PropTypes.object.isRequired,
|
||||
toolbox: PropTypes.object.isRequired,
|
||||
},
|
||||
|
||||
render() {
|
||||
let { toolbox, frame } = this.props;
|
||||
|
||||
let url = new URL(frame.source);
|
||||
let spec = url.toString();
|
||||
let func = frame.functionDisplayFrame || "";
|
||||
let tooltip = `${func} (${spec}:${frame.line}:${frame.column})`;
|
||||
let onClick = () => toolbox.viewSourceInDebugger(spec, frame.line);
|
||||
|
||||
let fields = [
|
||||
dom.span({ className: "frame-link-function-display-name" }, func),
|
||||
dom.a({ className: "frame-link-filename", onClick }, url.fileName),
|
||||
dom.span({ className: "frame-link-colon" }, ":"),
|
||||
dom.span({ className: "frame-link-line" }, frame.line),
|
||||
dom.span({ className: "frame-link-colon" }, ":"),
|
||||
dom.span({ className: "frame-link-column" }, frame.column)
|
||||
];
|
||||
|
||||
if (url.scheme === "http" || url.scheme === "https" || url.scheme === "ftp") {
|
||||
fields.push(dom.span({ className: "frame-link-host" }, url.host));
|
||||
}
|
||||
|
||||
return dom.span({ className: "frame-link", title: tooltip }, ...fields);
|
||||
}
|
||||
});
|
@ -37,13 +37,13 @@ function createParentMap (node, aggregator=Object.create(null)) {
|
||||
* @param {CensusTreeNode} census
|
||||
* @return {Object}
|
||||
*/
|
||||
function createTreeProperties (census) {
|
||||
function createTreeProperties (census, toolbox) {
|
||||
let map = createParentMap(census);
|
||||
|
||||
return {
|
||||
getParent: node => map(node.id),
|
||||
getParent: node => map[node.id],
|
||||
getChildren: node => node.children || [],
|
||||
renderItem: (item, depth, focused, arrow) => new TreeItem({ item, depth, focused, arrow }),
|
||||
renderItem: (item, depth, focused, arrow) => new TreeItem({ toolbox, item, depth, focused, arrow }),
|
||||
getRoots: () => census.children,
|
||||
getKey: node => node.id,
|
||||
itemHeight: HEAP_TREE_ROW_HEIGHT,
|
||||
@ -68,7 +68,7 @@ const Heap = module.exports = createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
let { snapshot, onSnapshotClick } = this.props;
|
||||
let { snapshot, onSnapshotClick, toolbox } = this.props;
|
||||
let census = snapshot ? snapshot.census : null;
|
||||
let state = snapshot ? snapshot.state : "initial";
|
||||
let statusText = snapshot ? getSnapshotStatusTextFull(snapshot) : "";
|
||||
@ -76,14 +76,14 @@ const Heap = module.exports = createClass({
|
||||
|
||||
switch (state) {
|
||||
case "initial":
|
||||
content = dom.button({
|
||||
content = [dom.button({
|
||||
className: "devtools-toolbarbutton take-snapshot",
|
||||
onClick: onSnapshotClick,
|
||||
// Want to use the [standalone] tag to leverage our styles,
|
||||
// but React hates that evidently
|
||||
"data-standalone": true,
|
||||
"data-text-only": true,
|
||||
}, TAKE_SNAPSHOT_TEXT)
|
||||
}, TAKE_SNAPSHOT_TEXT)];
|
||||
break;
|
||||
case states.ERROR:
|
||||
content = [
|
||||
@ -96,7 +96,7 @@ const Heap = module.exports = createClass({
|
||||
case states.READING:
|
||||
case states.READ:
|
||||
case states.SAVING_CENSUS:
|
||||
content = dom.span({ className: "snapshot-status devtools-throbber" }, statusText)
|
||||
content = [dom.span({ className: "snapshot-status devtools-throbber" }, statusText)];
|
||||
break;
|
||||
case states.SAVED_CENSUS:
|
||||
content = [
|
||||
@ -107,11 +107,11 @@ const Heap = module.exports = createClass({
|
||||
dom.span({ className: "heap-tree-item-total-count" }, "Total Count"),
|
||||
dom.span({ className: "heap-tree-item-name" }, "Name")
|
||||
),
|
||||
Tree(createTreeProperties(snapshot.census))
|
||||
Tree(createTreeProperties(snapshot.census, toolbox))
|
||||
];
|
||||
break;
|
||||
}
|
||||
let pane = dom.div({ className: "heap-view-panel", "data-state": state }, content);
|
||||
let pane = dom.div({ className: "heap-view-panel", "data-state": state }, ...content);
|
||||
|
||||
return (
|
||||
dom.div({ id: "heap-view", "data-state": state }, pane)
|
||||
|
@ -22,9 +22,9 @@ const List = module.exports = createClass({
|
||||
let { items, onClick, itemComponent: Item } = this.props;
|
||||
|
||||
return (
|
||||
dom.ul({ className: "list" }, items.map((item, index) => {
|
||||
dom.ul({ className: "list" }, ...items.map((item, index) => {
|
||||
return Item({
|
||||
item, index, onClick: () => onClick(item),
|
||||
key: index, item, index, onClick: () => onClick(item),
|
||||
});
|
||||
}))
|
||||
);
|
||||
|
@ -4,6 +4,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'frame.js',
|
||||
'heap.js',
|
||||
'list.js',
|
||||
'snapshot-list-item.js',
|
||||
|
@ -2,7 +2,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { DOM, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
|
||||
const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
|
||||
|
||||
const models = require("../models");
|
||||
|
||||
@ -33,29 +33,29 @@ const Toolbar = module.exports = createClass({
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
DOM.div({ className: "devtools-toolbar" }, [
|
||||
DOM.button({ className: `take-snapshot devtools-button`, onClick: onTakeSnapshotClick }),
|
||||
dom.div({ className: "devtools-toolbar" },
|
||||
dom.button({ className: `take-snapshot devtools-button`, onClick: onTakeSnapshotClick }),
|
||||
|
||||
DOM.label({},
|
||||
dom.label({},
|
||||
"Breakdown by ",
|
||||
DOM.select({
|
||||
dom.select({
|
||||
className: `select-breakdown`,
|
||||
onChange: e => onBreakdownChange(e.target.value),
|
||||
}, breakdowns.map(({ name, displayName }) => DOM.option({ value: name }, displayName)))
|
||||
}, ...breakdowns.map(({ name, displayName }) => dom.option({ key: name, value: name }, displayName)))
|
||||
),
|
||||
|
||||
DOM.label({}, [
|
||||
DOM.input({
|
||||
dom.label({},
|
||||
dom.input({
|
||||
type: "checkbox",
|
||||
checked: inverted,
|
||||
onChange: onToggleInverted,
|
||||
}),
|
||||
// TODO bug 1214799
|
||||
"Invert tree"
|
||||
]),
|
||||
),
|
||||
|
||||
DOM.label({}, [
|
||||
DOM.input({
|
||||
dom.label({},
|
||||
dom.input({
|
||||
type: "checkbox",
|
||||
checked: allocations.recording,
|
||||
disabled: allocations.togglingInProgress,
|
||||
@ -63,8 +63,8 @@ const Toolbar = module.exports = createClass({
|
||||
}),
|
||||
// TODO bug 1214799
|
||||
"Record allocation stacks"
|
||||
])
|
||||
])
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -3,7 +3,8 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { isSavedFrame } = require("devtools/shared/DevToolsUtils");
|
||||
const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
|
||||
const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
|
||||
const FrameView = createFactory(require("./frame"));
|
||||
|
||||
const INDENT = 10;
|
||||
const MAX_SOURCE_LENGTH = 200;
|
||||
@ -17,36 +18,23 @@ const TreeItem = module.exports = createClass({
|
||||
displayName: "tree-item",
|
||||
|
||||
render() {
|
||||
let { item, depth, arrow, focused } = this.props;
|
||||
let { item, depth, arrow, focused, toolbox } = this.props;
|
||||
|
||||
return dom.div({ className: `heap-tree-item ${focused ? "focused" :""}` },
|
||||
dom.span({ className: "heap-tree-item-bytes" }, item.bytes),
|
||||
dom.span({ className: "heap-tree-item-count" }, item.count),
|
||||
dom.span({ className: "heap-tree-item-total-bytes" }, item.totalBytes),
|
||||
dom.span({ className: "heap-tree-item-total-count" }, item.totalCount),
|
||||
dom.span({ className: "heap-tree-item-name", style: { marginLeft: depth * INDENT }},
|
||||
dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }, item.bytes),
|
||||
dom.span({ className: "heap-tree-item-field heap-tree-item-count" }, item.count),
|
||||
dom.span({ className: "heap-tree-item-field heap-tree-item-total-bytes" }, item.totalBytes),
|
||||
dom.span({ className: "heap-tree-item-field heap-tree-item-total-count" }, item.totalCount),
|
||||
dom.span({ className: "heap-tree-item-field heap-tree-item-name", style: { marginLeft: depth * INDENT }},
|
||||
arrow,
|
||||
this.toLabel(item.name)
|
||||
this.toLabel(item.name, toolbox)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
toLabel(name) {
|
||||
toLabel(name, toolbox) {
|
||||
return isSavedFrame(name)
|
||||
? this.savedFrameToLabel(name)
|
||||
? FrameView({ frame: name, toolbox })
|
||||
: String(name);
|
||||
},
|
||||
|
||||
savedFrameToLabel(frame) {
|
||||
return [
|
||||
dom.span({ className: "heap-tree-item-function-display-name" },
|
||||
frame.functionDisplayFrame || ""),
|
||||
dom.span({ className: "heap-tree-item-at" }, "@"),
|
||||
dom.span({ className: "heap-tree-item-source" }, frame.source.slice(0, MAX_SOURCE_LENGTH)),
|
||||
dom.span({ className: "heap-tree-item-colon" }, ":"),
|
||||
dom.span({ className: "heap-tree-item-line" }, frame.line),
|
||||
dom.span({ className: "heap-tree-item-colon" }, ":"),
|
||||
dom.span({ className: "heap-tree-item-column" }, frame.column)
|
||||
];
|
||||
}
|
||||
});
|
||||
|
@ -45,7 +45,7 @@ const ArrowExpander = createFactory(createClass({
|
||||
const TreeNode = createFactory(createClass({
|
||||
componentDidUpdate() {
|
||||
if (this.props.focused) {
|
||||
this.refs.button.getDOMNode().focus();
|
||||
this.refs.button.focus();
|
||||
}
|
||||
},
|
||||
|
||||
@ -244,7 +244,7 @@ const Tree = module.exports = createClass({
|
||||
*/
|
||||
_updateHeight() {
|
||||
this.setState({
|
||||
height: this.refs.tree.getDOMNode().clientHeight
|
||||
height: this.refs.tree.clientHeight
|
||||
});
|
||||
},
|
||||
|
||||
@ -361,8 +361,8 @@ const Tree = module.exports = createClass({
|
||||
*/
|
||||
_onScroll(e) {
|
||||
this.setState({
|
||||
scroll: Math.max(this.refs.tree.getDOMNode().scrollTop, 0),
|
||||
height: this.refs.tree.getDOMNode().clientHeight
|
||||
scroll: Math.max(this.refs.tree.scrollTop, 0),
|
||||
height: this.refs.tree.clientHeight
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -29,6 +29,9 @@ actions.SELECT_SNAPSHOT = "select-snapshot";
|
||||
// Fired to toggle tree inversion on or off.
|
||||
actions.TOGGLE_INVERTED = "toggle-inverted";
|
||||
|
||||
// Fired to set a new breakdown.
|
||||
actions.SET_BREAKDOWN = "set-breakdown";
|
||||
|
||||
// Fired when there is an error processing a snapshot or taking a census.
|
||||
actions.SNAPSHOT_ERROR = "snapshot-error";
|
||||
|
||||
|
@ -9,26 +9,55 @@ const BrowserLoaderModule = {};
|
||||
Cu.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule);
|
||||
const { require } = BrowserLoaderModule.BrowserLoader("resource://devtools/client/memory/", this);
|
||||
const { Task } = require("resource://gre/modules/Task.jsm");
|
||||
const { createFactory, createElement, render } = require("devtools/client/shared/vendor/react");
|
||||
const { createFactory, createElement, render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react");
|
||||
const { Provider } = require("devtools/client/shared/vendor/react-redux");
|
||||
const App = createFactory(require("devtools/client/memory/app"));
|
||||
const Store = require("devtools/client/memory/store");
|
||||
const { assert } = require("devtools/shared/DevToolsUtils");
|
||||
|
||||
/**
|
||||
* The current target, toolbox, MemoryFront, and HeapAnalysesClient, set by this tool's host.
|
||||
*/
|
||||
var gToolbox, gTarget, gFront, gHeapAnalysesClient;
|
||||
|
||||
function initialize () {
|
||||
return Task.spawn(function*() {
|
||||
let root = document.querySelector("#app");
|
||||
let store = Store();
|
||||
let app = createElement(App, { front: gFront, heapWorker: gHeapAnalysesClient });
|
||||
let provider = createElement(Provider, { store }, app);
|
||||
render(provider, root);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Variables set by `initialize()`
|
||||
*/
|
||||
var gStore, gRoot, gApp, gProvider, unsubscribe, isHighlighted;
|
||||
|
||||
function destroy () {
|
||||
return Task.spawn(function*(){});
|
||||
var initialize = Task.async(function*() {
|
||||
gRoot = document.querySelector("#app");
|
||||
gStore = Store();
|
||||
gApp = createElement(App, { toolbox: gToolbox, front: gFront, heapWorker: gHeapAnalysesClient });
|
||||
gProvider = createElement(Provider, { store: gStore }, gApp);
|
||||
render(gProvider, gRoot);
|
||||
unsubscribe = gStore.subscribe(onStateChange);
|
||||
});
|
||||
|
||||
var destroy = Task.async(function*() {
|
||||
const ok = unmountComponentAtNode(gRoot);
|
||||
assert(ok, "Should successfully unmount the memory tool's top level React component");
|
||||
|
||||
unsubscribe();
|
||||
|
||||
gStore, gRoot, gApp, gProvider, unsubscribe, isHighlighted = null;
|
||||
});
|
||||
|
||||
/**
|
||||
* Fired on any state change, currently only handles toggling
|
||||
* the highlighting of the tool when recording allocations.
|
||||
*/
|
||||
function onStateChange () {
|
||||
let isRecording = gStore.getState().allocations.recording;
|
||||
if (isRecording === isHighlighted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRecording) {
|
||||
gToolbox.highlightTool("memory");
|
||||
} else {
|
||||
gToolbox.unhighlightTool("memory");
|
||||
}
|
||||
|
||||
isHighlighted = isRecording;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ let breakdownModel = exports.breakdown = PropTypes.shape({
|
||||
/**
|
||||
* Snapshot model.
|
||||
*/
|
||||
let stateKeys = Object.keys(states).map(state => states[state]);
|
||||
let snapshotModel = exports.snapshot = PropTypes.shape({
|
||||
// Unique ID for a snapshot
|
||||
id: PropTypes.number.isRequired,
|
||||
@ -37,19 +38,18 @@ let snapshotModel = exports.snapshot = PropTypes.shape({
|
||||
error: PropTypes.object,
|
||||
// State the snapshot is in
|
||||
// @see ./constants.js
|
||||
state: function (props, propName) {
|
||||
let stateNames = Object.keys(states);
|
||||
let current = props.state;
|
||||
state: function (snapshot, propName) {
|
||||
let current = snapshot.state;
|
||||
let shouldHavePath = [states.SAVED, states.READ, states.SAVING_CENSUS, states.SAVED_CENSUS];
|
||||
let shouldHaveCensus = [states.SAVED_CENSUS];
|
||||
|
||||
if (!stateNames.includes(current)) {
|
||||
throw new Error(`Snapshot state must be one of ${stateNames}.`);
|
||||
if (!stateKeys.includes(current)) {
|
||||
throw new Error(`Snapshot state must be one of ${stateKeys}.`);
|
||||
}
|
||||
if (shouldHavePath.includes(current) && !path) {
|
||||
if (shouldHavePath.includes(current) && !snapshot.path) {
|
||||
throw new Error(`Snapshots in state ${current} must have a snapshot path.`);
|
||||
}
|
||||
if (shouldHaveCensus.includes(current) && (!props.census || !props.breakdown)) {
|
||||
if (shouldHaveCensus.includes(current) && (!snapshot.census || !snapshot.breakdown)) {
|
||||
throw new Error(`Snapshots in state ${current} must have a census and breakdown.`);
|
||||
}
|
||||
},
|
||||
|
@ -3,5 +3,11 @@ tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
head.js
|
||||
doc_steady_allocation.html
|
||||
|
||||
[browser_memory_allocationStackBreakdown_01.js]
|
||||
[browser_memory-breakdowns-01.js]
|
||||
skip-if = debug # bug 1219554
|
||||
[browser_memory-simple-01.js]
|
||||
skip-if = debug # bug 1219554
|
||||
[browser_memory_transferHeapSnapshot_e10s_01.js]
|
||||
|
@ -0,0 +1,33 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that the heap tree renders rows based on the breakdown
|
||||
*/
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const { gStore, document } = panel.panelWin;
|
||||
const $$ = document.querySelectorAll.bind(document);
|
||||
|
||||
yield takeSnapshot(panel.panelWin);
|
||||
|
||||
yield waitUntilSnapshotState(gStore, [states.SAVED_CENSUS]);
|
||||
|
||||
info("Check coarse type heap view");
|
||||
["objects", "other", "scripts", "strings"].forEach(findNameCell);
|
||||
|
||||
yield setBreakdown(panel.panelWin, "objectClass");
|
||||
info("Check object class heap view");
|
||||
["Function", "Object"].forEach(findNameCell);
|
||||
|
||||
yield setBreakdown(panel.panelWin, "internalType");
|
||||
info("Check internal type heap view");
|
||||
["JSObject"].forEach(findNameCell);
|
||||
|
||||
function findNameCell (name) {
|
||||
let el = Array.prototype.find.call($$(".tree .heap-tree-item-name span"), el => el.textContent === name);
|
||||
ok(el, `Found heap tree item cell for ${name}.`);
|
||||
}
|
||||
});
|
@ -0,0 +1,35 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests taking snapshots and default states.
|
||||
*/
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const { gStore, document } = panel.panelWin;
|
||||
const { getState, dispatch } = gStore;
|
||||
|
||||
let snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
|
||||
is(getState().snapshots.length, 0, "Starts with no snapshots in store");
|
||||
is(snapshotEls.length, 0, "No snapshots rendered");
|
||||
|
||||
yield takeSnapshot(panel.panelWin);
|
||||
snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
|
||||
is(getState().snapshots.length, 1, "One snapshot was created in store");
|
||||
is(snapshotEls.length, 1, "One snapshot was rendered");
|
||||
ok(snapshotEls[0].classList.contains("selected"), "Only snapshot has `selected` class");
|
||||
|
||||
yield takeSnapshot(panel.panelWin);
|
||||
snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
|
||||
is(getState().snapshots.length, 2, "Two snapshots created in store");
|
||||
is(snapshotEls.length, 2, "Two snapshots rendered");
|
||||
ok(!snapshotEls[0].classList.contains("selected"), "First snapshot no longer has `selected` class");
|
||||
ok(snapshotEls[1].classList.contains("selected"), "Second snapshot has `selected` class");
|
||||
|
||||
yield waitUntilSnapshotState(gStore, [states.SAVED_CENSUS, states.SAVED_CENSUS]);
|
||||
|
||||
ok(document.querySelector(".heap-tree-item-name"),
|
||||
"Should have rendered some tree items");
|
||||
});
|
@ -0,0 +1,39 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Sanity test that we can show allocation stack breakdowns in the tree.
|
||||
|
||||
"use strict";
|
||||
|
||||
const { waitForTime } = require("devtools/shared/DevToolsUtils");
|
||||
const { breakdowns } = require("devtools/client/memory/constants");
|
||||
const { toggleRecordingAllocationStacks } = require("devtools/client/memory/actions/allocations");
|
||||
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
|
||||
const breakdownActions = require("devtools/client/memory/actions/breakdown");
|
||||
const { toggleInverted } = require("devtools/client/memory/actions/inverted");
|
||||
|
||||
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
|
||||
|
||||
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
|
||||
const heapWorker = panel.panelWin.gHeapAnalysesClient;
|
||||
const front = panel.panelWin.gFront;
|
||||
const { getState, dispatch } = panel.panelWin.gStore;
|
||||
|
||||
dispatch(toggleInverted());
|
||||
ok(getState().inverted, true);
|
||||
|
||||
dispatch(breakdownActions.setBreakdown(breakdowns.allocationStack.breakdown));
|
||||
is(getState().breakdown.by, "allocationStack");
|
||||
|
||||
yield dispatch(toggleRecordingAllocationStacks(front));
|
||||
ok(getState().allocations.recording);
|
||||
|
||||
// Let some allocations build up.
|
||||
yield waitForTime(500);
|
||||
|
||||
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
|
||||
|
||||
const doc = panel.panelWin.document;
|
||||
ok(doc.querySelector(".frame-link-function-display-name"),
|
||||
"Should have rendered some allocation stack tree items");
|
||||
});
|
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
var objects = window.objects = [];
|
||||
|
||||
var allocate = this.allocate = function allocate() {
|
||||
for (var i = 0; i < 100; i++)
|
||||
objects.push({});
|
||||
setTimeout(allocate, 10);
|
||||
}
|
||||
|
||||
allocate();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -8,6 +8,9 @@ Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
|
||||
this);
|
||||
|
||||
var { snapshotState: states } = require("devtools/client/memory/constants");
|
||||
var { breakdownEquals, breakdownNameToSpec } = require("devtools/client/memory/utils");
|
||||
|
||||
Services.prefs.setBoolPref("devtools.memory.enabled", true);
|
||||
|
||||
/**
|
||||
@ -63,3 +66,60 @@ function makeMemoryTest(url, generator) {
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function waitUntilState (store, predicate) {
|
||||
let deferred = promise.defer();
|
||||
let unsubscribe = store.subscribe(check);
|
||||
|
||||
function check () {
|
||||
if (predicate(store.getState())) {
|
||||
unsubscribe();
|
||||
deferred.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
// Fire the check immediately incase the action has already occurred
|
||||
check();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function waitUntilSnapshotState (store, expected) {
|
||||
let predicate = () => {
|
||||
let snapshots = store.getState().snapshots;
|
||||
info(snapshots.map(x => x.state));
|
||||
return snapshots.length === expected.length &&
|
||||
expected.every((state, i) => state === "*" || snapshots[i].state === state);
|
||||
};
|
||||
info(`Waiting for snapshots to be of state: ${expected}`);
|
||||
return waitUntilState(store, predicate);
|
||||
}
|
||||
|
||||
function takeSnapshot (window) {
|
||||
let { gStore, document } = window;
|
||||
let snapshotCount = gStore.getState().snapshots.length;
|
||||
info(`Taking snapshot...`);
|
||||
document.querySelector(".devtools-toolbar .take-snapshot").click();
|
||||
return waitUntilState(gStore, () => gStore.getState().snapshots.length === snapshotCount + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets breakdown and waits for currently selected breakdown to use it
|
||||
* and be completed the census.
|
||||
*/
|
||||
function setBreakdown (window, type) {
|
||||
info(`Setting breakdown to ${type}...`);
|
||||
let { gStore, gHeapAnalysesClient } = window;
|
||||
// XXX: Should handle this via clicking the DOM, but React doesn't
|
||||
// fire the onChange event, so just change it in the store.
|
||||
// window.document.querySelector(`.select-breakdown`).value = type;
|
||||
gStore.dispatch(require("devtools/client/memory/actions/breakdown")
|
||||
.setBreakdownAndRefresh(gHeapAnalysesClient, breakdownNameToSpec(type)));
|
||||
|
||||
return waitUntilState(window.gStore, () => {
|
||||
let selected = window.gStore.getState().snapshots.find(s => s.selected);
|
||||
return selected.state === states.SAVED_CENSUS &&
|
||||
breakdownEquals(breakdownNameToSpec(type), selected.breakdown);
|
||||
});
|
||||
}
|
||||
|
7
devtools/client/themes/images/tool-memory-active.svg
Normal file
7
devtools/client/themes/images/tool-memory-active.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<!-- 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/. -->
|
||||
<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#71c054">
|
||||
<path opacity="0.2" d="M5.8 3.9l4.9.1v8.3H5.8z"/>
|
||||
<path d="M12.8 8L14 9.2c.1.1.3.2.5.2s.4-.1.5-.2c.3-.3.3-.8 0-1.1l-1.6-1.6c-.2-.2-.5-.3-.8-.2l-.6.4V5.3l.8-.3L14 6.2c.1.1.3.2.5.2s.4-.1.5-.2c.3-.3.3-.8 0-1.1l-1.6-1.6c-.2-.2-.5-.3-.8-.2l-.6.4V2.3c0-.2-.1-.3-.3-.3H4.3c-.2 0-.3.1-.3.3v1.5l-.8-.4c-.3-.1-.6-.1-.9.1L.8 5.1c-.3.3-.3.8 0 1.1.1.1.3.2.5.2s.4-.1.5-.2L3 5l1 .4v1.4l-.8-.4c-.3-.1-.6-.1-.9.1L.8 8.1c-.3.3-.3.8 0 1.1.1.1.3.2.5.2s.4-.1.5-.2L3 8l1 .4v1.4l-.8-.4c-.3-.1-.6-.1-.9.1L.7 11.1c-.3.3-.3.8 0 1.1.1.1.3.2.5.2s.4-.1.5-.2L3 11l1 .4v2.4c0 .1.1.2.3.2h7.5c.1 0 .3-.1.3-.3v-2.4l.8-.3 1.2 1.2c.1.1.3.2.5.2s.4-.1.5-.2c.3-.3.3-.8 0-1.1l-1.6-1.6c-.2-.2-.5-.3-.8-.2l-.7.4V8.3l.8-.3zM10 12H6V4h4v8z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1021 B |
7
devtools/client/themes/images/tool-memory.svg
Normal file
7
devtools/client/themes/images/tool-memory.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<!-- 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/. -->
|
||||
<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="whitesmoke">
|
||||
<path opacity="0.2" d="M5.8 3.9l4.9.1v8.3H5.8z"/>
|
||||
<path d="M12.8 8L14 9.2c.1.1.3.2.5.2s.4-.1.5-.2c.3-.3.3-.8 0-1.1l-1.6-1.6c-.2-.2-.5-.3-.8-.2l-.6.4V5.3l.8-.3L14 6.2c.1.1.3.2.5.2s.4-.1.5-.2c.3-.3.3-.8 0-1.1l-1.6-1.6c-.2-.2-.5-.3-.8-.2l-.6.4V2.3c0-.2-.1-.3-.3-.3H4.3c-.2 0-.3.1-.3.3v1.5l-.8-.4c-.3-.1-.6-.1-.9.1L.8 5.1c-.3.3-.3.8 0 1.1.1.1.3.2.5.2s.4-.1.5-.2L3 5l1 .4v1.4l-.8-.4c-.3-.1-.6-.1-.9.1L.8 8.1c-.3.3-.3.8 0 1.1.1.1.3.2.5.2s.4-.1.5-.2L3 8l1 .4v1.4l-.8-.4c-.3-.1-.6-.1-.9.1L.7 11.1c-.3.3-.3.8 0 1.1.1.1.3.2.5.2s.4-.1.5-.2L3 11l1 .4v2.4c0 .1.1.2.3.2h7.5c.1 0 .3-.1.3-.3v-2.4l.8-.3 1.2 1.2c.1.1.3.2.5.2s.4-.1.5-.2c.3-.3.3-.8 0-1.1l-1.6-1.6c-.2-.2-.5-.3-.8-.2l-.7.4V8.3l.8-.3zM10 12H6V4h4v8z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -122,7 +122,6 @@ html, .theme-body, #app, #memory-tool, #memory-tool-container {
|
||||
border-bottom: 1px solid rgba(128,128,128,0.15);
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
color: var(--theme-selection-color);
|
||||
}
|
||||
|
||||
.list > li.selected {
|
||||
@ -219,7 +218,7 @@ html, .theme-body, #app, #memory-tool, #memory-tool-container {
|
||||
height: var(--heap-tree-row-height);
|
||||
}
|
||||
|
||||
.heap-tree-item span, .header span {
|
||||
.heap-tree-item-field, .header span {
|
||||
float: left;
|
||||
}
|
||||
|
||||
@ -268,7 +267,7 @@ html, .theme-body, #app, #memory-tool, #memory-tool-container {
|
||||
}
|
||||
|
||||
.heap-tree-item-name {
|
||||
padding-left: 10px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.error::before {
|
||||
@ -287,3 +286,34 @@ html, .theme-body, #app, #memory-tool, #memory-tool-container {
|
||||
.theme-light .error::before {
|
||||
background-image: url(chrome://devtools/skin/themes/images/webconsole.svg#light-icons);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frame View components
|
||||
*/
|
||||
|
||||
.focused .frame-link-filename,
|
||||
.focused .frame-link-column,
|
||||
.focused .frame-link-line,
|
||||
.focused .frame-link-host,
|
||||
.focused .frame-link-colon {
|
||||
color: var(--theme-selection-color);
|
||||
}
|
||||
|
||||
.frame-link-filename {
|
||||
color: var(--theme-highlight-blue);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.frame-link-filename:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.frame-link-column, .frame-link-line, .frame-link-colon {
|
||||
color: var(--theme-highlight-orange);
|
||||
}
|
||||
|
||||
.frame-link-host {
|
||||
font-size: 90%;
|
||||
margin-left: 5px;
|
||||
color: var(--theme-content-color2);
|
||||
}
|
||||
|
@ -173,6 +173,9 @@ RootActor.prototype = {
|
||||
// Whether or not `getProfile()` supports specifying a `startTime`
|
||||
// and `endTime` to filter out samples. Fx40+
|
||||
profilerDataFilterable: true,
|
||||
// Whether or not the MemoryActor's heap snapshot abilities are
|
||||
// fully equipped to handle heap snapshots for the memory tool. Fx44+
|
||||
heapSnapshots: true,
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -55,9 +55,11 @@ using JS::ubi::AtomOrTwoByteChars;
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(HeapSnapshot)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HeapSnapshot)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HeapSnapshot)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.util.Log;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.text.TextUtils;
|
||||
@ -14,6 +13,7 @@ public class SiteIdentity {
|
||||
private final String LOGTAG = "GeckoSiteIdentity";
|
||||
private SecurityMode mSecurityMode;
|
||||
private boolean mSecure;
|
||||
private boolean mLoginInsecure;
|
||||
private MixedMode mMixedModeActive;
|
||||
private MixedMode mMixedModeDisplay;
|
||||
private TrackingMode mTrackingMode;
|
||||
@ -134,6 +134,7 @@ public class SiteIdentity {
|
||||
mSupplemental = null;
|
||||
mVerifier = null;
|
||||
mSecure = false;
|
||||
mLoginInsecure = false;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
@ -220,6 +221,14 @@ public class SiteIdentity {
|
||||
return mSecure;
|
||||
}
|
||||
|
||||
public void setLoginInsecure(boolean isInsecure) {
|
||||
mLoginInsecure = isInsecure;
|
||||
}
|
||||
|
||||
public boolean loginInsecure() {
|
||||
return mLoginInsecure;
|
||||
}
|
||||
|
||||
public MixedMode getMixedModeActive() {
|
||||
return mMixedModeActive;
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
import org.mozilla.gecko.widget.SiteLogins;
|
||||
|
||||
public class Tab {
|
||||
@ -508,6 +507,10 @@ public class Tab {
|
||||
mSiteIdentity.update(identityData);
|
||||
}
|
||||
|
||||
public void setLoginInsecure(boolean isInsecure) {
|
||||
mSiteIdentity.setLoginInsecure(isInsecure);
|
||||
}
|
||||
|
||||
public void setSiteLogins(SiteLogins siteLogins) {
|
||||
mSiteLogins = siteLogins;
|
||||
}
|
||||
|
@ -99,6 +99,7 @@ public class Tabs implements GeckoEventListener {
|
||||
"Tab:Close",
|
||||
"Tab:Select",
|
||||
"Content:LocationChange",
|
||||
"Content:LoginInsecure",
|
||||
"Content:SecurityChange",
|
||||
"Content:StateChange",
|
||||
"Content:LoadError",
|
||||
@ -510,6 +511,9 @@ public class Tabs implements GeckoEventListener {
|
||||
} else if (event.equals("Content:SecurityChange")) {
|
||||
tab.updateIdentityData(message.getJSONObject("identity"));
|
||||
notifyListeners(tab, TabEvents.SECURITY_CHANGE);
|
||||
} else if (event.equals("Content:LoginInsecure")) {
|
||||
tab.setLoginInsecure(true);
|
||||
notifyListeners(tab, TabEvents.SECURITY_CHANGE);
|
||||
} else if (event.equals("Content:StateChange")) {
|
||||
int state = message.getInt("state");
|
||||
if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) {
|
||||
|
@ -589,6 +589,7 @@ with that structure, consider a translation which ignores the preceding domain a
|
||||
just addresses the organization to follow, e.g. "This site is run by " -->
|
||||
<!ENTITY identity_connection_secure "Secure Connection">
|
||||
<!ENTITY identity_connection_insecure "Insecure connection">
|
||||
<!ENTITY identity_login_insecure "This page is not secure and your login could be vulnerable.">
|
||||
|
||||
<!-- Mixed content notifications in site identity popup -->
|
||||
<!ENTITY mixed_content_blocked_all1 "&brandShortName; has blocked insecure content on this page.">
|
||||
|
@ -494,6 +494,7 @@
|
||||
<!-- Site identity popup -->
|
||||
<string name="identity_connection_secure">&identity_connection_secure;</string>
|
||||
<string name="identity_connection_insecure">&identity_connection_insecure;</string>
|
||||
<string name="identity_login_insecure">&identity_login_insecure;</string>
|
||||
|
||||
<string name="mixed_content_blocked_all">&mixed_content_blocked_all1;</string>
|
||||
<string name="mixed_content_blocked_some">&mixed_content_blocked_some1;</string>
|
||||
|
@ -36,6 +36,7 @@ public class TabReceivedService extends IntentService {
|
||||
|
||||
public TabReceivedService() {
|
||||
super(LOGTAG);
|
||||
setIntentRedelivery(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -74,6 +75,10 @@ public class TabReceivedService extends IntentService {
|
||||
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
||||
notificationManager.notify(notificationId, builder.build());
|
||||
|
||||
// Save the ID last so if the Service is killed and the Intent is redelivered,
|
||||
// the ID is unlikely to have been updated and we would re-use the the old one.
|
||||
// This would prevent two identical notifications from appearing if the
|
||||
// notification was shown during the previous Intent processing attempt.
|
||||
prefs.edit().putInt(PREF_NOTIFICATION_ID, notificationId).apply();
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,8 @@ import org.mozilla.gecko.widget.SiteLogins;
|
||||
/**
|
||||
* SiteIdentityPopup is a singleton class that displays site identity data in
|
||||
* an arrow panel popup hanging from the lock icon in the browser toolbar.
|
||||
*
|
||||
* A site identity icon may be displayed in the url, and is set in <code>ToolbarDisplayLayout</code>.
|
||||
*/
|
||||
public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListener {
|
||||
|
||||
@ -309,7 +311,14 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
|
||||
*/
|
||||
private void updateConnectionState(final SiteIdentity siteIdentity) {
|
||||
if (!siteIdentity.isSecure()) {
|
||||
if (siteIdentity.getMixedModeActive() == MixedMode.MIXED_CONTENT_LOADED) {
|
||||
if (siteIdentity.loginInsecure()) {
|
||||
// Login detected on an insecure page.
|
||||
mIcon.setImageResource(R.drawable.lock_disabled);
|
||||
clearSecurityStateIcon();
|
||||
|
||||
mMixedContentActivity.setVisibility(View.VISIBLE);
|
||||
mMixedContentActivity.setText(R.string.identity_login_insecure);
|
||||
} else if (siteIdentity.getMixedModeActive() == MixedMode.MIXED_CONTENT_LOADED) {
|
||||
// Active Mixed Content loaded because user has disabled blocking.
|
||||
mIcon.setImageResource(R.drawable.lock_disabled);
|
||||
clearSecurityStateIcon();
|
||||
|
@ -484,16 +484,19 @@ public class ToolbarDisplayLayout extends ThemedLinearLayout
|
||||
final MixedMode activeMixedMode;
|
||||
final MixedMode displayMixedMode;
|
||||
final TrackingMode trackingMode;
|
||||
final boolean loginInsecure;
|
||||
if (siteIdentity == null) {
|
||||
securityMode = SecurityMode.UNKNOWN;
|
||||
activeMixedMode = MixedMode.UNKNOWN;
|
||||
displayMixedMode = MixedMode.UNKNOWN;
|
||||
trackingMode = TrackingMode.UNKNOWN;
|
||||
loginInsecure = false;
|
||||
} else {
|
||||
securityMode = siteIdentity.getSecurityMode();
|
||||
activeMixedMode = siteIdentity.getMixedModeActive();
|
||||
displayMixedMode = siteIdentity.getMixedModeDisplay();
|
||||
trackingMode = siteIdentity.getTrackingMode();
|
||||
loginInsecure = siteIdentity.loginInsecure();
|
||||
}
|
||||
|
||||
// This is a bit tricky, but we have one icon and three potential indicators.
|
||||
@ -501,7 +504,9 @@ public class ToolbarDisplayLayout extends ThemedLinearLayout
|
||||
int imageLevel = securityMode.ordinal();
|
||||
|
||||
// Check to see if any protection was overridden first
|
||||
if (trackingMode == TrackingMode.TRACKING_CONTENT_LOADED) {
|
||||
if (loginInsecure) {
|
||||
imageLevel = LEVEL_LOCK_DISABLED;
|
||||
} else if (trackingMode == TrackingMode.TRACKING_CONTENT_LOADED) {
|
||||
imageLevel = LEVEL_SHIELD_DISABLED;
|
||||
} else if (trackingMode == TrackingMode.TRACKING_CONTENT_BLOCKED) {
|
||||
imageLevel = LEVEL_SHIELD_ENABLED;
|
||||
|
@ -32,7 +32,7 @@
|
||||
<h1 class="showNormal">&privatebrowsingpage.title.normal1;</h1>
|
||||
|
||||
<div class="contentSection">
|
||||
<p class="showPrivate">&privatebrowsingpage.description.private4;</p>
|
||||
<p class="showPrivate">&privatebrowsingpage.description.trackingProtection;<br /><br />&privatebrowsingpage.description.privateDetails;</p>
|
||||
<p class="showNormal">&privatebrowsingpage.description.normal2;</p>
|
||||
|
||||
<p class="showPrivate"><a href="https://support.mozilla.org/kb/private-browsing-firefox-android">&privatebrowsingpage.link.private;</a></p>
|
||||
|
@ -4763,6 +4763,7 @@ var BrowserEventHandler = {
|
||||
|
||||
InitLater(() => BrowserApp.deck.addEventListener("click", InputWidgetHelper, true));
|
||||
InitLater(() => BrowserApp.deck.addEventListener("click", SelectHelper, true));
|
||||
InitLater(() => BrowserApp.deck.addEventListener("InsecureLoginFormsStateChange", IdentityHandler.sendLoginInsecure, true));
|
||||
|
||||
// ReaderViews support backPress listeners.
|
||||
Messaging.addListener(() => {
|
||||
@ -6617,6 +6618,18 @@ var IdentityHandler = {
|
||||
return this.TRACKING_MODE_UNKNOWN;
|
||||
},
|
||||
|
||||
sendLoginInsecure: function sendLoginInsecure() {
|
||||
let loginInsecure = LoginManagerParent.hasInsecureLoginForms(BrowserApp.selectedBrowser);
|
||||
if (loginInsecure) {
|
||||
let message = {
|
||||
type: "Content:LoginInsecure",
|
||||
tabID: BrowserApp.selectedTab.id
|
||||
};
|
||||
Messaging.sendRequest(message);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
shieldHistogramAdd: function(browser, value) {
|
||||
if (PrivateBrowsingUtils.isBrowserPrivate(browser)) {
|
||||
return;
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* 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/. */
|
||||
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
@ -12,7 +11,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
/* Constants for password prompt telemetry.
|
||||
* Mirrored in nsLoginManagerPrompter.js */
|
||||
* Mirrored in nsLoginManagerPrompter.js */
|
||||
const PROMPT_DISPLAYED = 0;
|
||||
|
||||
const PROMPT_ADD = 1;
|
||||
@ -34,429 +33,407 @@ function LoginManagerPrompter() {
|
||||
}
|
||||
|
||||
LoginManagerPrompter.prototype = {
|
||||
classID : Components.ID("97d12931-abe2-11df-94e2-0800200c9a66"),
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsILoginManagerPrompter]),
|
||||
|
||||
classID : Components.ID("97d12931-abe2-11df-94e2-0800200c9a66"),
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsILoginManagerPrompter]),
|
||||
_factory : null,
|
||||
_window : null,
|
||||
_debug : false, // mirrors signon.debug
|
||||
|
||||
_factory : null,
|
||||
_window : null,
|
||||
_debug : false, // mirrors signon.debug
|
||||
__pwmgr : null, // Password Manager service
|
||||
get _pwmgr() {
|
||||
if (!this.__pwmgr)
|
||||
this.__pwmgr = Cc["@mozilla.org/login-manager;1"].
|
||||
getService(Ci.nsILoginManager);
|
||||
return this.__pwmgr;
|
||||
},
|
||||
|
||||
__pwmgr : null, // Password Manager service
|
||||
get _pwmgr() {
|
||||
if (!this.__pwmgr)
|
||||
this.__pwmgr = Cc["@mozilla.org/login-manager;1"].
|
||||
getService(Ci.nsILoginManager);
|
||||
return this.__pwmgr;
|
||||
},
|
||||
__promptService : null, // Prompt service for user interaction
|
||||
get _promptService() {
|
||||
if (!this.__promptService)
|
||||
this.__promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
|
||||
getService(Ci.nsIPromptService2);
|
||||
return this.__promptService;
|
||||
},
|
||||
|
||||
__promptService : null, // Prompt service for user interaction
|
||||
get _promptService() {
|
||||
if (!this.__promptService)
|
||||
this.__promptService =
|
||||
Cc["@mozilla.org/embedcomp/prompt-service;1"].
|
||||
getService(Ci.nsIPromptService2);
|
||||
return this.__promptService;
|
||||
},
|
||||
|
||||
__strBundle : null, // String bundle for L10N
|
||||
get _strBundle() {
|
||||
if (!this.__strBundle) {
|
||||
var bunService = Cc["@mozilla.org/intl/stringbundle;1"].
|
||||
getService(Ci.nsIStringBundleService);
|
||||
this.__strBundle = {
|
||||
pwmgr : bunService.createBundle(
|
||||
"chrome://passwordmgr/locale/passwordmgr.properties"),
|
||||
brand : bunService.createBundle("chrome://branding/locale/brand.properties")
|
||||
};
|
||||
__strBundle : null, // String bundle for L10N
|
||||
get _strBundle() {
|
||||
if (!this.__strBundle) {
|
||||
var bunService = Cc["@mozilla.org/intl/stringbundle;1"].
|
||||
getService(Ci.nsIStringBundleService);
|
||||
this.__strBundle = {
|
||||
pwmgr : bunService.createBundle("chrome://passwordmgr/locale/passwordmgr.properties"),
|
||||
brand : bunService.createBundle("chrome://branding/locale/brand.properties")
|
||||
};
|
||||
|
||||
if (!this.__strBundle)
|
||||
throw "String bundle for Login Manager not present!";
|
||||
if (!this.__strBundle)
|
||||
throw "String bundle for Login Manager not present!";
|
||||
}
|
||||
|
||||
return this.__strBundle;
|
||||
},
|
||||
|
||||
__ellipsis : null,
|
||||
get _ellipsis() {
|
||||
if (!this.__ellipsis) {
|
||||
this.__ellipsis = "\u2026";
|
||||
try {
|
||||
this.__ellipsis = Services.prefs.getComplexValue(
|
||||
"intl.ellipsis", Ci.nsIPrefLocalizedString).data;
|
||||
} catch (e) { }
|
||||
}
|
||||
return this.__ellipsis;
|
||||
},
|
||||
|
||||
/*
|
||||
* log
|
||||
*
|
||||
* Internal function for logging debug messages to the Error Console window.
|
||||
*/
|
||||
log : function (message) {
|
||||
if (!this._debug)
|
||||
return;
|
||||
|
||||
dump("Pwmgr Prompter: " + message + "\n");
|
||||
Services.console.logStringMessage("Pwmgr Prompter: " + message);
|
||||
},
|
||||
|
||||
/* ---------- nsILoginManagerPrompter prompts ---------- */
|
||||
|
||||
/*
|
||||
* init
|
||||
*
|
||||
*/
|
||||
init : function (aWindow, aFactory) {
|
||||
this._window = aWindow;
|
||||
this._factory = aFactory || null;
|
||||
|
||||
var prefBranch = Services.prefs.getBranch("signon.");
|
||||
this._debug = prefBranch.getBoolPref("debug");
|
||||
this.log("===== initialized =====");
|
||||
},
|
||||
|
||||
setE10sData : function (aBrowser, aOpener) {
|
||||
throw new Error("This should be filled in when Android is multiprocess");
|
||||
},
|
||||
|
||||
/*
|
||||
* promptToSavePassword
|
||||
*
|
||||
*/
|
||||
promptToSavePassword : function (aLogin) {
|
||||
this._showSaveLoginNotification(aLogin);
|
||||
Services.telemetry.getHistogramById("PWMGR_PROMPT_REMEMBER_ACTION").add(PROMPT_DISPLAYED);
|
||||
},
|
||||
|
||||
/*
|
||||
* _showLoginNotification
|
||||
*
|
||||
* Displays a notification doorhanger.
|
||||
* @param aBody
|
||||
* String message to be displayed in the doorhanger
|
||||
* @param aButtons
|
||||
* Buttons to display with the doorhanger
|
||||
* @param aUsername
|
||||
* Username string used in creating a doorhanger action
|
||||
* @param aPassword
|
||||
* Password string used in creating a doorhanger action
|
||||
*/
|
||||
_showLoginNotification : function (aBody, aButtons, aUsername, aPassword) {
|
||||
let notifyWin = this._window.top;
|
||||
let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject;
|
||||
let browser = chromeWin.BrowserApp.getBrowserForWindow(notifyWin);
|
||||
let tabID = chromeWin.BrowserApp.getTabForBrowser(browser).id;
|
||||
|
||||
let actionText = {
|
||||
text: aUsername,
|
||||
type: "EDIT",
|
||||
bundle: { username: aUsername,
|
||||
password: aPassword }
|
||||
};
|
||||
|
||||
// The page we're going to hasn't loaded yet, so we want to persist
|
||||
// across the first location change.
|
||||
|
||||
// Sites like Gmail perform a funky redirect dance before you end up
|
||||
// at the post-authentication page. I don't see a good way to
|
||||
// heuristically determine when to ignore such location changes, so
|
||||
// we'll try ignoring location changes based on a time interval.
|
||||
let options = {
|
||||
persistWhileVisible: true,
|
||||
timeout: Date.now() + 10000,
|
||||
actionText: actionText
|
||||
}
|
||||
|
||||
var nativeWindow = this._getNativeWindow();
|
||||
if (nativeWindow)
|
||||
nativeWindow.doorhanger.show(aBody, "password", aButtons, tabID, options, "LOGIN");
|
||||
},
|
||||
|
||||
/*
|
||||
* _showSaveLoginNotification
|
||||
*
|
||||
* Displays a notification doorhanger (rather than a popup), to allow the user to
|
||||
* save the specified login. This allows the user to see the results of
|
||||
* their login, and only save a login which they know worked.
|
||||
*
|
||||
*/
|
||||
_showSaveLoginNotification : function (aLogin) {
|
||||
let brandShortName = this._strBundle.brand.GetStringFromName("brandShortName");
|
||||
let notificationText = this._getLocalizedString("saveLogin", [brandShortName]);
|
||||
|
||||
let username = aLogin.username ? this._sanitizeUsername(aLogin.username) : "";
|
||||
|
||||
// The callbacks in |buttons| have a closure to access the variables
|
||||
// in scope here; set one to |this._pwmgr| so we can get back to pwmgr
|
||||
// without a getService() call.
|
||||
var pwmgr = this._pwmgr;
|
||||
let promptHistogram = Services.telemetry.getHistogramById("PWMGR_PROMPT_REMEMBER_ACTION");
|
||||
|
||||
var buttons = [
|
||||
{
|
||||
label: this._getLocalizedString("neverButton"),
|
||||
callback: function() {
|
||||
promptHistogram.add(PROMPT_NEVER);
|
||||
pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this._getLocalizedString("rememberButton"),
|
||||
callback: function(checked, response) {
|
||||
if (response) {
|
||||
aLogin.username = response["username"] || aLogin.username;
|
||||
aLogin.password = response["password"] || aLogin.password;
|
||||
}
|
||||
pwmgr.addLogin(aLogin);
|
||||
promptHistogram.add(PROMPT_ADD);
|
||||
},
|
||||
positive: true
|
||||
}
|
||||
];
|
||||
|
||||
return this.__strBundle;
|
||||
},
|
||||
this._showLoginNotification(notificationText, buttons, aLogin.username, aLogin.password);
|
||||
},
|
||||
|
||||
/*
|
||||
* promptToChangePassword
|
||||
*
|
||||
* Called when we think we detect a password change for an existing
|
||||
* login, when the form being submitted contains multiple password
|
||||
* fields.
|
||||
*
|
||||
*/
|
||||
promptToChangePassword : function (aOldLogin, aNewLogin) {
|
||||
this._showChangeLoginNotification(aOldLogin, aNewLogin.password);
|
||||
Services.telemetry.getHistogramById("PWMGR_PROMPT_UPDATE_ACTION").add(PROMPT_DISPLAYED);
|
||||
},
|
||||
|
||||
__ellipsis : null,
|
||||
get _ellipsis() {
|
||||
if (!this.__ellipsis) {
|
||||
this.__ellipsis = "\u2026";
|
||||
try {
|
||||
this.__ellipsis = Services.prefs.getComplexValue(
|
||||
"intl.ellipsis", Ci.nsIPrefLocalizedString).data;
|
||||
} catch (e) { }
|
||||
/*
|
||||
* _showChangeLoginNotification
|
||||
*
|
||||
* Shows the Change Password notification doorhanger.
|
||||
*
|
||||
*/
|
||||
_showChangeLoginNotification : function (aOldLogin, aNewPassword) {
|
||||
var notificationText;
|
||||
if (aOldLogin.username) {
|
||||
let displayUser = this._sanitizeUsername(aOldLogin.username);
|
||||
notificationText = this._getLocalizedString("updatePassword", [displayUser]);
|
||||
} else {
|
||||
notificationText = this._getLocalizedString("updatePasswordNoUser");
|
||||
}
|
||||
|
||||
// The callbacks in |buttons| have a closure to access the variables
|
||||
// in scope here; set one to |this._pwmgr| so we can get back to pwmgr
|
||||
// without a getService() call.
|
||||
var self = this;
|
||||
let promptHistogram = Services.telemetry.getHistogramById("PWMGR_PROMPT_UPDATE_ACTION");
|
||||
|
||||
var buttons = [
|
||||
{
|
||||
label: this._getLocalizedString("dontUpdateButton"),
|
||||
callback: function() {
|
||||
promptHistogram.add(PROMPT_NOTNOW);
|
||||
// do nothing
|
||||
}
|
||||
return this.__ellipsis;
|
||||
},
|
||||
},
|
||||
{
|
||||
label: this._getLocalizedString("updateButton"),
|
||||
callback: function(checked, response) {
|
||||
let password = response ? response["password"] : aNewPassword;
|
||||
self._updateLogin(aOldLogin, password);
|
||||
|
||||
promptHistogram.add(PROMPT_UPDATE);
|
||||
},
|
||||
positive: true
|
||||
}
|
||||
];
|
||||
|
||||
/*
|
||||
* log
|
||||
*
|
||||
* Internal function for logging debug messages to the Error Console window.
|
||||
*/
|
||||
log : function (message) {
|
||||
if (!this._debug)
|
||||
return;
|
||||
this._showLoginNotification(notificationText, buttons, aOldLogin.username, aNewPassword);
|
||||
},
|
||||
|
||||
dump("Pwmgr Prompter: " + message + "\n");
|
||||
Services.console.logStringMessage("Pwmgr Prompter: " + message);
|
||||
},
|
||||
/*
|
||||
* promptToChangePasswordWithUsernames
|
||||
*
|
||||
* Called when we detect a password change in a form submission, but we
|
||||
* don't know which existing login (username) it's for. Asks the user
|
||||
* to select a username and confirm the password change.
|
||||
*
|
||||
* Note: The caller doesn't know the username for aNewLogin, so this
|
||||
* function fills in .username and .usernameField with the values
|
||||
* from the login selected by the user.
|
||||
*
|
||||
* Note; XPCOM stupidity: |count| is just |logins.length|.
|
||||
*/
|
||||
promptToChangePasswordWithUsernames : function (logins, count, aNewLogin) {
|
||||
const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
|
||||
|
||||
var usernames = logins.map(l => l.username);
|
||||
var dialogText = this._getLocalizedString("userSelectText");
|
||||
var dialogTitle = this._getLocalizedString("passwordChangeTitle");
|
||||
var selectedIndex = { value: null };
|
||||
|
||||
/* ---------- nsILoginManagerPrompter prompts ---------- */
|
||||
// If user selects ok, outparam.value is set to the index
|
||||
// of the selected username.
|
||||
var ok = this._promptService.select(null,
|
||||
dialogTitle, dialogText,
|
||||
usernames.length, usernames,
|
||||
selectedIndex);
|
||||
if (ok) {
|
||||
// Now that we know which login to use, modify its password.
|
||||
var selectedLogin = logins[selectedIndex.value];
|
||||
this.log("Updating password for user " + selectedLogin.username);
|
||||
this._updateLogin(selectedLogin, aNewLogin.password);
|
||||
}
|
||||
},
|
||||
|
||||
/* ---------- Internal Methods ---------- */
|
||||
|
||||
/*
|
||||
* _updateLogin
|
||||
*/
|
||||
_updateLogin : function (login, newPassword) {
|
||||
var now = Date.now();
|
||||
var propBag = Cc["@mozilla.org/hash-property-bag;1"].
|
||||
createInstance(Ci.nsIWritablePropertyBag);
|
||||
if (newPassword) {
|
||||
propBag.setProperty("password", newPassword);
|
||||
// Explicitly set the password change time here (even though it would
|
||||
// be changed automatically), to ensure that it's exactly the same
|
||||
// value as timeLastUsed.
|
||||
propBag.setProperty("timePasswordChanged", now);
|
||||
}
|
||||
propBag.setProperty("timeLastUsed", now);
|
||||
propBag.setProperty("timesUsedIncrement", 1);
|
||||
this._pwmgr.modifyLogin(login, propBag);
|
||||
},
|
||||
|
||||
/*
|
||||
* _getChromeWindow
|
||||
*
|
||||
* Given a content DOM window, returns the chrome window it's in.
|
||||
*/
|
||||
_getChromeWindow: function (aWindow) {
|
||||
var chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell)
|
||||
.chromeEventHandler.ownerDocument.defaultView;
|
||||
return chromeWin;
|
||||
},
|
||||
|
||||
/*
|
||||
* init
|
||||
*
|
||||
*/
|
||||
init : function (aWindow, aFactory) {
|
||||
this._window = aWindow;
|
||||
this._factory = aFactory || null;
|
||||
/*
|
||||
* _getNativeWindow
|
||||
*
|
||||
* Returns the NativeWindow to this prompter, or null if there isn't
|
||||
* a NativeWindow available (w/ error sent to logcat).
|
||||
*/
|
||||
_getNativeWindow : function () {
|
||||
let nativeWindow = null;
|
||||
try {
|
||||
let notifyWin = this._window.top;
|
||||
let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject;
|
||||
if (chromeWin.NativeWindow) {
|
||||
nativeWindow = chromeWin.NativeWindow;
|
||||
} else {
|
||||
Cu.reportError("NativeWindow not available on window");
|
||||
}
|
||||
|
||||
var prefBranch = Services.prefs.getBranch("signon.");
|
||||
this._debug = prefBranch.getBoolPref("debug");
|
||||
this.log("===== initialized =====");
|
||||
},
|
||||
} catch (e) {
|
||||
// If any errors happen, just assume no native window helper.
|
||||
Cu.reportError("No NativeWindow available: " + e);
|
||||
}
|
||||
return nativeWindow;
|
||||
},
|
||||
|
||||
setE10sData : function (aBrowser, aOpener) {
|
||||
throw new Error("This should be filled in when Android is multiprocess");
|
||||
},
|
||||
/*
|
||||
* _getLocalizedString
|
||||
*
|
||||
* Can be called as:
|
||||
* _getLocalizedString("key1");
|
||||
* _getLocalizedString("key2", ["arg1"]);
|
||||
* _getLocalizedString("key3", ["arg1", "arg2"]);
|
||||
* (etc)
|
||||
*
|
||||
* Returns the localized string for the specified key,
|
||||
* formatted if required.
|
||||
*
|
||||
*/
|
||||
_getLocalizedString : function (key, formatArgs) {
|
||||
if (formatArgs)
|
||||
return this._strBundle.pwmgr.formatStringFromName(
|
||||
key, formatArgs, formatArgs.length);
|
||||
else
|
||||
return this._strBundle.pwmgr.GetStringFromName(key);
|
||||
},
|
||||
|
||||
/*
|
||||
* promptToSavePassword
|
||||
*
|
||||
*/
|
||||
promptToSavePassword : function (aLogin) {
|
||||
this._showSaveLoginNotification(aLogin);
|
||||
Services.telemetry.getHistogramById("PWMGR_PROMPT_REMEMBER_ACTION").add(PROMPT_DISPLAYED);
|
||||
},
|
||||
/*
|
||||
* _sanitizeUsername
|
||||
*
|
||||
* Sanitizes the specified username, by stripping quotes and truncating if
|
||||
* it's too long. This helps prevent an evil site from messing with the
|
||||
* "save password?" prompt too much.
|
||||
*/
|
||||
_sanitizeUsername : function (username) {
|
||||
if (username.length > 30) {
|
||||
username = username.substring(0, 30);
|
||||
username += this._ellipsis;
|
||||
}
|
||||
return username.replace(/['"]/g, "");
|
||||
},
|
||||
|
||||
/*
|
||||
* _getFormattedHostname
|
||||
*
|
||||
* The aURI parameter may either be a string uri, or an nsIURI instance.
|
||||
*
|
||||
* Returns the hostname to use in a nsILoginInfo object (for example,
|
||||
* "http://example.com").
|
||||
*/
|
||||
_getFormattedHostname : function (aURI) {
|
||||
var uri;
|
||||
if (aURI instanceof Ci.nsIURI) {
|
||||
uri = aURI;
|
||||
} else {
|
||||
uri = Services.io.newURI(aURI, null, null);
|
||||
}
|
||||
var scheme = uri.scheme;
|
||||
|
||||
/*
|
||||
* _showLoginNotification
|
||||
*
|
||||
* Displays a notification doorhanger.
|
||||
* @param aBody
|
||||
* String message to be displayed in the doorhanger
|
||||
* @param aButtons
|
||||
* Buttons to display with the doorhanger
|
||||
* @param aUsername
|
||||
* Username string used in creating a doorhanger action
|
||||
* @param aPassword
|
||||
* Password string used in creating a doorhanger action
|
||||
*/
|
||||
_showLoginNotification : function (aBody, aButtons, aUsername, aPassword) {
|
||||
let notifyWin = this._window.top;
|
||||
let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject;
|
||||
let browser = chromeWin.BrowserApp.getBrowserForWindow(notifyWin);
|
||||
let tabID = chromeWin.BrowserApp.getTabForBrowser(browser).id;
|
||||
var hostname = scheme + "://" + uri.host;
|
||||
|
||||
let actionText = {
|
||||
text: aUsername,
|
||||
type: "EDIT",
|
||||
bundle: { username: aUsername,
|
||||
password: aPassword }
|
||||
};
|
||||
// If the URI explicitly specified a port, only include it when
|
||||
// it's not the default. (We never want "http://foo.com:80")
|
||||
let port = uri.port;
|
||||
if (port != -1) {
|
||||
var handler = Services.io.getProtocolHandler(scheme);
|
||||
if (port != handler.defaultPort)
|
||||
hostname += ":" + port;
|
||||
}
|
||||
|
||||
// The page we're going to hasn't loaded yet, so we want to persist
|
||||
// across the first location change.
|
||||
|
||||
// Sites like Gmail perform a funky redirect dance before you end up
|
||||
// at the post-authentication page. I don't see a good way to
|
||||
// heuristically determine when to ignore such location changes, so
|
||||
// we'll try ignoring location changes based on a time interval.
|
||||
let options = {
|
||||
persistWhileVisible: true,
|
||||
timeout: Date.now() + 10000,
|
||||
actionText: actionText
|
||||
}
|
||||
|
||||
var nativeWindow = this._getNativeWindow();
|
||||
if (nativeWindow)
|
||||
nativeWindow.doorhanger.show(aBody, "password", aButtons, tabID, options, "LOGIN");
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* _showSaveLoginNotification
|
||||
*
|
||||
* Displays a notification doorhanger (rather than a popup), to allow the user to
|
||||
* save the specified login. This allows the user to see the results of
|
||||
* their login, and only save a login which they know worked.
|
||||
*
|
||||
*/
|
||||
_showSaveLoginNotification : function (aLogin) {
|
||||
let brandShortName = this._strBundle.brand.GetStringFromName("brandShortName");
|
||||
let notificationText = this._getLocalizedString("saveLogin", [brandShortName]);
|
||||
|
||||
let username = aLogin.username ? this._sanitizeUsername(aLogin.username) : "";
|
||||
|
||||
// The callbacks in |buttons| have a closure to access the variables
|
||||
// in scope here; set one to |this._pwmgr| so we can get back to pwmgr
|
||||
// without a getService() call.
|
||||
var pwmgr = this._pwmgr;
|
||||
let promptHistogram = Services.telemetry.getHistogramById("PWMGR_PROMPT_REMEMBER_ACTION");
|
||||
|
||||
var buttons = [
|
||||
{
|
||||
label: this._getLocalizedString("neverButton"),
|
||||
callback: function() {
|
||||
promptHistogram.add(PROMPT_NEVER);
|
||||
pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this._getLocalizedString("rememberButton"),
|
||||
callback: function(checked, response) {
|
||||
|
||||
if (response) {
|
||||
aLogin.username = response["username"] || aLogin.username;
|
||||
aLogin.password = response["password"] || aLogin.password;
|
||||
}
|
||||
pwmgr.addLogin(aLogin);
|
||||
promptHistogram.add(PROMPT_ADD);
|
||||
},
|
||||
positive: true
|
||||
}
|
||||
];
|
||||
|
||||
this._showLoginNotification(notificationText, buttons, aLogin.username, aLogin.password);
|
||||
},
|
||||
|
||||
/*
|
||||
* promptToChangePassword
|
||||
*
|
||||
* Called when we think we detect a password change for an existing
|
||||
* login, when the form being submitted contains multiple password
|
||||
* fields.
|
||||
*
|
||||
*/
|
||||
promptToChangePassword : function (aOldLogin, aNewLogin) {
|
||||
this._showChangeLoginNotification(aOldLogin, aNewLogin.password);
|
||||
Services.telemetry.getHistogramById("PWMGR_PROMPT_UPDATE_ACTION").add(PROMPT_DISPLAYED);
|
||||
},
|
||||
|
||||
/*
|
||||
* _showChangeLoginNotification
|
||||
*
|
||||
* Shows the Change Password notification doorhanger.
|
||||
*
|
||||
*/
|
||||
_showChangeLoginNotification : function (aOldLogin, aNewPassword) {
|
||||
var notificationText;
|
||||
if (aOldLogin.username) {
|
||||
let displayUser = this._sanitizeUsername(aOldLogin.username);
|
||||
notificationText = this._getLocalizedString("updatePassword", [displayUser]);
|
||||
} else {
|
||||
notificationText = this._getLocalizedString("updatePasswordNoUser");
|
||||
}
|
||||
|
||||
// The callbacks in |buttons| have a closure to access the variables
|
||||
// in scope here; set one to |this._pwmgr| so we can get back to pwmgr
|
||||
// without a getService() call.
|
||||
var self = this;
|
||||
let promptHistogram = Services.telemetry.getHistogramById("PWMGR_PROMPT_UPDATE_ACTION");
|
||||
|
||||
var buttons = [
|
||||
{
|
||||
label: this._getLocalizedString("dontUpdateButton"),
|
||||
callback: function() {
|
||||
promptHistogram.add(PROMPT_NOTNOW);
|
||||
// do nothing
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this._getLocalizedString("updateButton"),
|
||||
callback: function(checked, response) {
|
||||
let password = response ? response["password"] : aNewPassword;
|
||||
self._updateLogin(aOldLogin, password);
|
||||
|
||||
promptHistogram.add(PROMPT_UPDATE);
|
||||
},
|
||||
positive: true
|
||||
}
|
||||
];
|
||||
|
||||
this._showLoginNotification(notificationText, buttons, aOldLogin.username, aNewPassword);
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* promptToChangePasswordWithUsernames
|
||||
*
|
||||
* Called when we detect a password change in a form submission, but we
|
||||
* don't know which existing login (username) it's for. Asks the user
|
||||
* to select a username and confirm the password change.
|
||||
*
|
||||
* Note: The caller doesn't know the username for aNewLogin, so this
|
||||
* function fills in .username and .usernameField with the values
|
||||
* from the login selected by the user.
|
||||
*
|
||||
* Note; XPCOM stupidity: |count| is just |logins.length|.
|
||||
*/
|
||||
promptToChangePasswordWithUsernames : function (logins, count, aNewLogin) {
|
||||
const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
|
||||
|
||||
var usernames = logins.map(l => l.username);
|
||||
var dialogText = this._getLocalizedString("userSelectText");
|
||||
var dialogTitle = this._getLocalizedString("passwordChangeTitle");
|
||||
var selectedIndex = { value: null };
|
||||
|
||||
// If user selects ok, outparam.value is set to the index
|
||||
// of the selected username.
|
||||
var ok = this._promptService.select(null,
|
||||
dialogTitle, dialogText,
|
||||
usernames.length, usernames,
|
||||
selectedIndex);
|
||||
if (ok) {
|
||||
// Now that we know which login to use, modify its password.
|
||||
var selectedLogin = logins[selectedIndex.value];
|
||||
this.log("Updating password for user " + selectedLogin.username);
|
||||
this._updateLogin(selectedLogin, aNewLogin.password);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
/* ---------- Internal Methods ---------- */
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* _updateLogin
|
||||
*/
|
||||
_updateLogin : function (login, newPassword) {
|
||||
var now = Date.now();
|
||||
var propBag = Cc["@mozilla.org/hash-property-bag;1"].
|
||||
createInstance(Ci.nsIWritablePropertyBag);
|
||||
if (newPassword) {
|
||||
propBag.setProperty("password", newPassword);
|
||||
// Explicitly set the password change time here (even though it would
|
||||
// be changed automatically), to ensure that it's exactly the same
|
||||
// value as timeLastUsed.
|
||||
propBag.setProperty("timePasswordChanged", now);
|
||||
}
|
||||
propBag.setProperty("timeLastUsed", now);
|
||||
propBag.setProperty("timesUsedIncrement", 1);
|
||||
this._pwmgr.modifyLogin(login, propBag);
|
||||
},
|
||||
|
||||
/*
|
||||
* _getChromeWindow
|
||||
*
|
||||
* Given a content DOM window, returns the chrome window it's in.
|
||||
*/
|
||||
_getChromeWindow: function (aWindow) {
|
||||
var chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell)
|
||||
.chromeEventHandler.ownerDocument.defaultView;
|
||||
return chromeWin;
|
||||
},
|
||||
|
||||
/*
|
||||
* _getNativeWindow
|
||||
*
|
||||
* Returns the NativeWindow to this prompter, or null if there isn't
|
||||
* a NativeWindow available (w/ error sent to logcat).
|
||||
*/
|
||||
_getNativeWindow : function () {
|
||||
let nativeWindow = null;
|
||||
try {
|
||||
let notifyWin = this._window.top;
|
||||
let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject;
|
||||
if (chromeWin.NativeWindow) {
|
||||
nativeWindow = chromeWin.NativeWindow;
|
||||
} else {
|
||||
Cu.reportError("NativeWindow not available on window");
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
// If any errors happen, just assume no native window helper.
|
||||
Cu.reportError("No NativeWindow available: " + e);
|
||||
}
|
||||
return nativeWindow;
|
||||
},
|
||||
|
||||
/*
|
||||
* _getLocalizedString
|
||||
*
|
||||
* Can be called as:
|
||||
* _getLocalizedString("key1");
|
||||
* _getLocalizedString("key2", ["arg1"]);
|
||||
* _getLocalizedString("key3", ["arg1", "arg2"]);
|
||||
* (etc)
|
||||
*
|
||||
* Returns the localized string for the specified key,
|
||||
* formatted if required.
|
||||
*
|
||||
*/
|
||||
_getLocalizedString : function (key, formatArgs) {
|
||||
if (formatArgs)
|
||||
return this._strBundle.pwmgr.formatStringFromName(
|
||||
key, formatArgs, formatArgs.length);
|
||||
else
|
||||
return this._strBundle.pwmgr.GetStringFromName(key);
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* _sanitizeUsername
|
||||
*
|
||||
* Sanitizes the specified username, by stripping quotes and truncating if
|
||||
* it's too long. This helps prevent an evil site from messing with the
|
||||
* "save password?" prompt too much.
|
||||
*/
|
||||
_sanitizeUsername : function (username) {
|
||||
if (username.length > 30) {
|
||||
username = username.substring(0, 30);
|
||||
username += this._ellipsis;
|
||||
}
|
||||
return username.replace(/['"]/g, "");
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* _getFormattedHostname
|
||||
*
|
||||
* The aURI parameter may either be a string uri, or an nsIURI instance.
|
||||
*
|
||||
* Returns the hostname to use in a nsILoginInfo object (for example,
|
||||
* "http://example.com").
|
||||
*/
|
||||
_getFormattedHostname : function (aURI) {
|
||||
var uri;
|
||||
if (aURI instanceof Ci.nsIURI) {
|
||||
uri = aURI;
|
||||
} else {
|
||||
uri = Services.io.newURI(aURI, null, null);
|
||||
}
|
||||
var scheme = uri.scheme;
|
||||
|
||||
var hostname = scheme + "://" + uri.host;
|
||||
|
||||
// If the URI explicitly specified a port, only include it when
|
||||
// it's not the default. (We never want "http://foo.com:80")
|
||||
let port = uri.port;
|
||||
if (port != -1) {
|
||||
var handler = Services.io.getProtocolHandler(scheme);
|
||||
if (port != handler.defaultPort)
|
||||
hostname += ":" + port;
|
||||
}
|
||||
|
||||
return hostname;
|
||||
},
|
||||
return hostname;
|
||||
},
|
||||
|
||||
}; // end of LoginManagerPrompter implementation
|
||||
|
||||
|
||||
var component = [LoginManagerPrompter];
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);
|
||||
|
||||
|
@ -36,7 +36,6 @@ android {
|
||||
androidTest {
|
||||
manifest.srcFile "${topobjdir}/build/mobile/robocop/AndroidManifest.xml"
|
||||
java {
|
||||
srcDir "src/robocop_harness"
|
||||
srcDir "src/robocop"
|
||||
srcDir "src/background"
|
||||
srcDir "src/browser"
|
||||
|
@ -13,7 +13,9 @@
|
||||
term. -->
|
||||
<!ENTITY privatebrowsingpage.title.normal1 "You are not in Private Browsing">
|
||||
|
||||
<!ENTITY privatebrowsingpage.description.private4 "&brandShortName; will prevent you from being tracked and won't remember any history, but downloaded files and new bookmarks will still be saved to your device.">
|
||||
<!ENTITY privatebrowsingpage.description.trackingProtection "&brandShortName; blocks parts of the pages that may track your browsing activity.">
|
||||
<!ENTITY privatebrowsingpage.description.privateDetails "We won't remember any history, but downloaded files and new bookmarks will still be saved to your device.">
|
||||
|
||||
<!-- Localization note (privatebrowsingpage.description.normal2): "Private
|
||||
Browsing is capitalized in English to be consistent with our existing uses
|
||||
of the term. -->
|
||||
|
@ -115,8 +115,7 @@ class MachCommands(MachCommandBase):
|
||||
srcdir('app/src/androidTest/res', 'build/mobile/robocop/res')
|
||||
srcdir('app/src/androidTest/assets', 'mobile/android/tests/browser/robocop/assets')
|
||||
# Test code.
|
||||
srcdir('app/src/robocop_harness/org/mozilla/gecko', 'build/mobile/robocop')
|
||||
srcdir('app/src/robocop/org/mozilla/gecko/tests', 'mobile/android/tests/browser/robocop')
|
||||
srcdir('app/src/robocop', 'mobile/android/tests/browser/robocop/src')
|
||||
srcdir('app/src/background', 'mobile/android/tests/background/junit3/src')
|
||||
srcdir('app/src/browser', 'mobile/android/tests/browser/junit3/src')
|
||||
srcdir('app/src/javaaddons', 'mobile/android/tests/javaaddons/src')
|
||||
|
@ -1,179 +1,179 @@
|
||||
[DEFAULT]
|
||||
subsuite = robocop
|
||||
|
||||
[testGeckoProfile.java]
|
||||
# [test_bug720538.java] # disabled on fig - bug 897072
|
||||
[testAboutPage.java]
|
||||
[src/org/mozilla/gecko/tests/testGeckoProfile.java]
|
||||
# [src/org/mozilla/gecko/tests/test_bug720538.java] # disabled on fig - bug 897072
|
||||
[src/org/mozilla/gecko/tests/testAboutPage.java]
|
||||
# disabled on Android 2.3; bug 975187
|
||||
skip-if = android_version == "10"
|
||||
[testAddonManager.java]
|
||||
[src/org/mozilla/gecko/tests/testAddonManager.java]
|
||||
# disabled on 2.3: bug 941624, bug 1063509, bug 1073374, bug 1087221, bug 1088023, bug 1088027, bug 1090206; on 4.3, bug 1144918
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
[testAddSearchEngine.java]
|
||||
[src/org/mozilla/gecko/tests/testAddSearchEngine.java]
|
||||
# disabled on Android 2.3, bug 979552; on 4.3, bug 1120759
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
[testAdobeFlash.java]
|
||||
[src/org/mozilla/gecko/tests/testAdobeFlash.java]
|
||||
# disabled on 4.3, bug 1146420
|
||||
skip-if = android_version == "18"
|
||||
[testANRReporter.java]
|
||||
[testAwesomebar.java]
|
||||
[testAxisLocking.java]
|
||||
[src/org/mozilla/gecko/tests/testANRReporter.java]
|
||||
[src/org/mozilla/gecko/tests/testAwesomebar.java]
|
||||
[src/org/mozilla/gecko/tests/testAxisLocking.java]
|
||||
# disabled on 4.3, bug 1144874
|
||||
skip-if = android_version == "18"
|
||||
# [testBookmark.java] # see bug 915350
|
||||
[testBookmarksPanel.java]
|
||||
# [src/org/mozilla/gecko/tests/testBookmark.java] # see bug 915350
|
||||
[src/org/mozilla/gecko/tests/testBookmarksPanel.java]
|
||||
# disabled on 2.3, bug 979615; on 4.3, bug 987930
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
[testBookmarkFolders.java]
|
||||
[src/org/mozilla/gecko/tests/testBookmarkFolders.java]
|
||||
# disabled on Android 2.3, bug 979552; on 4.3, bug 1144921
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
# [testBookmarklets.java] # see bug 915350
|
||||
# [testBookmarkKeyword.java] # see bug 915350
|
||||
[testBrowserProvider.java]
|
||||
[testBrowserSearchVisibility.java]
|
||||
[testClearPrivateData.java]
|
||||
# [src/org/mozilla/gecko/tests/testBookmarklets.java] # see bug 915350
|
||||
# [src/org/mozilla/gecko/tests/testBookmarkKeyword.java] # see bug 915350
|
||||
[src/org/mozilla/gecko/tests/testBrowserProvider.java]
|
||||
[src/org/mozilla/gecko/tests/testBrowserSearchVisibility.java]
|
||||
[src/org/mozilla/gecko/tests/testClearPrivateData.java]
|
||||
# disabled on 2.3, bug 948591; on 4.3, bug 1000643
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
[testDBUtils.java]
|
||||
[testDistribution.java]
|
||||
[testDoorHanger.java]
|
||||
[src/org/mozilla/gecko/tests/testDBUtils.java]
|
||||
[src/org/mozilla/gecko/tests/testDistribution.java]
|
||||
[src/org/mozilla/gecko/tests/testDoorHanger.java]
|
||||
# disabled on 2.3, bug 1085609; on 4.3, bug 1144924
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
[testFilterOpenTab.java]
|
||||
# [testFindInPage.java] # bug 1128287
|
||||
[testFlingCorrectness.java]
|
||||
[src/org/mozilla/gecko/tests/testFilterOpenTab.java]
|
||||
# [src/org/mozilla/gecko/tests/testFindInPage.java] # bug 1128287
|
||||
[src/org/mozilla/gecko/tests/testFlingCorrectness.java]
|
||||
# disabled on 4.3, bug 1144874
|
||||
skip-if = android_version == "18"
|
||||
[testFormHistory.java]
|
||||
[testGetUserMedia.java]
|
||||
[src/org/mozilla/gecko/tests/testFormHistory.java]
|
||||
[src/org/mozilla/gecko/tests/testGetUserMedia.java]
|
||||
# failures across the board, bug 1092202 & bug 1144926
|
||||
skip-if = true
|
||||
# [testHistory.java] # see bug 915350
|
||||
[testHomeBanner.java]
|
||||
[testImportFromAndroid.java]
|
||||
# [src/org/mozilla/gecko/tests/testHistory.java] # see bug 915350
|
||||
[src/org/mozilla/gecko/tests/testHomeBanner.java]
|
||||
[src/org/mozilla/gecko/tests/testImportFromAndroid.java]
|
||||
# disabled on 2.3 and 4.3 bug 979552
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
[testInputUrlBar.java]
|
||||
[src/org/mozilla/gecko/tests/testInputUrlBar.java]
|
||||
# disabled on 2.3 bug 1165511
|
||||
skip-if = android_version == "10"
|
||||
[testJarReader.java]
|
||||
[testLinkContextMenu.java]
|
||||
[src/org/mozilla/gecko/tests/testJarReader.java]
|
||||
[src/org/mozilla/gecko/tests/testLinkContextMenu.java]
|
||||
# disabled on 4.3, bug 1083666
|
||||
skip-if = android_version == "18"
|
||||
# [testHomeListsProvider.java] # see bug 952310
|
||||
[testLoad.java]
|
||||
[testMailToContextMenu.java]
|
||||
# [testMasterPassword.java] disabled for being finicky, see bug 1033013
|
||||
# [src/org/mozilla/gecko/tests/testHomeListsProvider.java] # see bug 952310
|
||||
[src/org/mozilla/gecko/tests/testLoad.java]
|
||||
[src/org/mozilla/gecko/tests/testMailToContextMenu.java]
|
||||
# [src/org/mozilla/gecko/tests/testMasterPassword.java] disabled for being finicky, see bug 1033013
|
||||
# disabled on 2.3; bug 979603
|
||||
# disabled on 4.0; bug 1006242
|
||||
# skip-if = android_version == "10" || android_version == "15"
|
||||
[testNewTab.java]
|
||||
[src/org/mozilla/gecko/tests/testNewTab.java]
|
||||
# disabled on 4.3, bug 1145851
|
||||
skip-if = android_version == "18"
|
||||
[testPanCorrectness.java]
|
||||
[src/org/mozilla/gecko/tests/testPanCorrectness.java]
|
||||
# disabled on 4.3, bug 1144874
|
||||
skip-if = android_version == "18"
|
||||
# [testPasswordEncrypt.java] # see bug 824067
|
||||
[testPasswordProvider.java]
|
||||
# [testPermissions.java] # see bug 757475
|
||||
[testPictureLinkContextMenu.java]
|
||||
# [src/org/mozilla/gecko/tests/testPasswordEncrypt.java] # see bug 824067
|
||||
[src/org/mozilla/gecko/tests/testPasswordProvider.java]
|
||||
# [src/org/mozilla/gecko/tests/testPermissions.java] # see bug 757475
|
||||
[src/org/mozilla/gecko/tests/testPictureLinkContextMenu.java]
|
||||
# disabled on 4.3, bug 1031496
|
||||
skip-if = android_version == "18"
|
||||
[testPrefsObserver.java]
|
||||
[testPrivateBrowsing.java]
|
||||
[testPromptGridInput.java]
|
||||
[src/org/mozilla/gecko/tests/testPrefsObserver.java]
|
||||
[src/org/mozilla/gecko/tests/testPrivateBrowsing.java]
|
||||
[src/org/mozilla/gecko/tests/testPromptGridInput.java]
|
||||
# bug 1001657 for 2.3 and 4.3
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
[testReadingListProvider.java]
|
||||
[testSearchHistoryProvider.java]
|
||||
[testSearchSuggestions.java]
|
||||
[src/org/mozilla/gecko/tests/testReadingListProvider.java]
|
||||
[src/org/mozilla/gecko/tests/testSearchHistoryProvider.java]
|
||||
[src/org/mozilla/gecko/tests/testSearchSuggestions.java]
|
||||
# disabled on 2.3, bug 907768; on 4.3, bug 1145867
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
[testSessionOOMSave.java]
|
||||
[src/org/mozilla/gecko/tests/testSessionOOMSave.java]
|
||||
# disabled on 2.3, bug 945395; on 4.3, bug 1144888
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
[testSessionOOMRestore.java]
|
||||
[src/org/mozilla/gecko/tests/testSessionOOMRestore.java]
|
||||
# disabled on Android 2.3, bug 979600; on 4.3, bug 1145879
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
[testSettingsMenuItems.java]
|
||||
[src/org/mozilla/gecko/tests/testSettingsMenuItems.java]
|
||||
# disabled on Android 2.3, bug 979552; on 4.3, bug 1144898
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
# [testShareLink.java] # see bug 915897
|
||||
[testSystemPages.java]
|
||||
# [src/org/mozilla/gecko/tests/testShareLink.java] # see bug 915897
|
||||
[src/org/mozilla/gecko/tests/testSystemPages.java]
|
||||
# disabled on 2.3, bug 979603; on 4.3, bug 1142811
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
# [testThumbnails.java] # see bug 813107
|
||||
[testTitleBar.java]
|
||||
# [src/org/mozilla/gecko/tests/testThumbnails.java] # see bug 813107
|
||||
[src/org/mozilla/gecko/tests/testTitleBar.java]
|
||||
# disabled on Android 2.3, bug 979552; on 4.3, bug 1145881
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
# [testVkbOverlap.java] # see bug 907274
|
||||
# [src/org/mozilla/gecko/tests/testVkbOverlap.java] # see bug 907274
|
||||
|
||||
# Using JavascriptTest
|
||||
# (If your test can be written entirely in Javascript, consider writing
|
||||
# it as a chrome test instead. See mobile/android/tests/browser/chrome.)
|
||||
[testBrowserDiscovery.java]
|
||||
[src/org/mozilla/gecko/tests/testBrowserDiscovery.java]
|
||||
# disabled on 4.3, bug 1158384
|
||||
skip-if = android_version == "18"
|
||||
[testFilePicker.java]
|
||||
[testHistoryService.java]
|
||||
[src/org/mozilla/gecko/tests/testFilePicker.java]
|
||||
[src/org/mozilla/gecko/tests/testHistoryService.java]
|
||||
# disabled on 4.3, bug 1116036
|
||||
skip-if = android_version == "18"
|
||||
# [testMozPay.java] # see bug 945675
|
||||
[testOrderedBroadcast.java]
|
||||
[testOSLocale.java]
|
||||
# [src/org/mozilla/gecko/tests/testMozPay.java] # see bug 945675
|
||||
[src/org/mozilla/gecko/tests/testOrderedBroadcast.java]
|
||||
[src/org/mozilla/gecko/tests/testOSLocale.java]
|
||||
# disabled on 2.3 and 4.3: Bug 1124494
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
[testReadingListCache.java]
|
||||
[testRestrictions.java]
|
||||
[testSnackbarAPI.java]
|
||||
[testTrackingProtection.java]
|
||||
[src/org/mozilla/gecko/tests/testReadingListCache.java]
|
||||
[src/org/mozilla/gecko/tests/testRestrictions.java]
|
||||
[src/org/mozilla/gecko/tests/testSnackbarAPI.java]
|
||||
[src/org/mozilla/gecko/tests/testTrackingProtection.java]
|
||||
# disabled on 4.3, bug 1158363
|
||||
skip-if = android_version == "18"
|
||||
[testUITelemetry.java]
|
||||
[testVideoControls.java]
|
||||
[src/org/mozilla/gecko/tests/testUITelemetry.java]
|
||||
[src/org/mozilla/gecko/tests/testVideoControls.java]
|
||||
# disabled on Android 2.3 due to video playback issues, bug 1088038; on 4.3, bug 1098532
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
|
||||
# Used for Talos, please don't use in mochitest
|
||||
#[testCheck2.java]
|
||||
#[testCheck3.java] # and the autophone version
|
||||
#[src/org/mozilla/gecko/tests/testCheck2.java]
|
||||
#[src/org/mozilla/gecko/tests/testCheck3.java] # and the autophone version
|
||||
|
||||
# Using UITest
|
||||
#[testAboutHomePageNavigation.java] # see bug 947550, bug 979038 and bug 977952
|
||||
[testAboutHomeVisibility.java]
|
||||
#[src/org/mozilla/gecko/tests/testAboutHomePageNavigation.java] # see bug 947550, bug 979038 and bug 977952
|
||||
[src/org/mozilla/gecko/tests/testAboutHomeVisibility.java]
|
||||
# disabled on Android 2.3; bug 946656
|
||||
skip-if = android_version == "10"
|
||||
[testAppMenuPathways.java]
|
||||
[src/org/mozilla/gecko/tests/testAppMenuPathways.java]
|
||||
# disabled on 4.3, bug 1158005
|
||||
skip-if = android_version == "18"
|
||||
[testBackButtonInEditMode.java]
|
||||
[testEventDispatcher.java]
|
||||
[testGeckoRequest.java]
|
||||
[testInputConnection.java]
|
||||
[src/org/mozilla/gecko/tests/testBackButtonInEditMode.java]
|
||||
[src/org/mozilla/gecko/tests/testEventDispatcher.java]
|
||||
[src/org/mozilla/gecko/tests/testGeckoRequest.java]
|
||||
[src/org/mozilla/gecko/tests/testInputConnection.java]
|
||||
# disabled on Android 2.3, 4.3; bug 1025968
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
[testJavascriptBridge.java]
|
||||
[testNativeCrypto.java]
|
||||
[testReaderModeTitle.java]
|
||||
[testSessionHistory.java]
|
||||
[src/org/mozilla/gecko/tests/testJavascriptBridge.java]
|
||||
[src/org/mozilla/gecko/tests/testNativeCrypto.java]
|
||||
[src/org/mozilla/gecko/tests/testReaderModeTitle.java]
|
||||
[src/org/mozilla/gecko/tests/testSessionHistory.java]
|
||||
# disabled on Android 4.3, bug 1144879
|
||||
skip-if = android_version == "18"
|
||||
[testStateWhileLoading.java]
|
||||
[src/org/mozilla/gecko/tests/testStateWhileLoading.java]
|
||||
|
||||
[testSelectionCarets.java]
|
||||
[src/org/mozilla/gecko/tests/testSelectionCarets.java]
|
||||
# testSelectionHandler disabled on Android 2.3 by trailing skip-if, due to bug 980074
|
||||
# also disabled on Android 4.3, bug 1144882
|
||||
[testSelectionHandler.java]
|
||||
[src/org/mozilla/gecko/tests/testSelectionHandler.java]
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
|
||||
# testInputSelections disabled on Android 2.3 by trailing skip-if, due to bug 980074
|
||||
[testInputSelections.java]
|
||||
[src/org/mozilla/gecko/tests/testInputSelections.java]
|
||||
skip-if = android_version == "10"
|
||||
|
||||
# testTextareaSelections disabled on Android 2.3 by trailing skip-if, due to bug 980074
|
||||
[testTextareaSelections.java]
|
||||
[src/org/mozilla/gecko/tests/testTextareaSelections.java]
|
||||
skip-if = android_version == "10"
|
||||
|
||||
# testStumblerSetting disabled on Android 4.3, bug 1145846
|
||||
[testStumblerSetting.java]
|
||||
[src/org/mozilla/gecko/tests/testStumblerSetting.java]
|
||||
skip-if = android_version == "10" || android_version == "18"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user