зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to b2g-inbound a=merge
This commit is contained in:
Коммит
3c1f665b48
|
@ -287,9 +287,8 @@ let gSyncUI = {
|
|||
// Commands
|
||||
doSync: function SUI_doSync() {
|
||||
let needsSetup = this._needsSetup();
|
||||
let loginFailed = this._loginFailed();
|
||||
|
||||
if (!(loginFailed || needsSetup)) {
|
||||
if (!needsSetup) {
|
||||
setTimeout(function () Weave.Service.errorHandler.syncAndReportErrors(), 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -448,10 +448,13 @@ this.GoogleImporter.prototype = {
|
|||
if (phoneNodes.length) {
|
||||
contact.tel = [];
|
||||
for (let [,phoneNode] of Iterator(phoneNodes)) {
|
||||
let phoneNumber = phoneNode.hasAttribute("uri") ?
|
||||
phoneNode.getAttribute("uri").replace("tel:", "") :
|
||||
phoneNode.firstChild.nodeValue;
|
||||
contact.tel.push({
|
||||
pref: (phoneNode.getAttribute("primary") == "true"),
|
||||
type: [getFieldType(phoneNode)],
|
||||
value: phoneNode.getAttribute("uri").replace("tel:", "")
|
||||
value: phoneNumber
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -790,16 +790,85 @@ let LoopContactsInternal = Object.freeze({
|
|||
|
||||
/**
|
||||
* Search through the data store for contacts that match a certain (sub-)string.
|
||||
* NB: The current implementation is very simple, naive if you will; we fetch
|
||||
* _all_ the contacts via `getAll()` and iterate over all of them to find
|
||||
* the contacts matching the supplied query (brute-force search in
|
||||
* exponential time).
|
||||
*
|
||||
* @param {String} query Needle to search for in our haystack of contacts
|
||||
* @param {Object} query Needle to search for in our haystack of contacts
|
||||
* @param {Function} callback Function that will be invoked once the operation
|
||||
* finished. The first argument passed will be an
|
||||
* `Error` object or `null`. The second argument will
|
||||
* be an `Array` of contact objects, if successfull.
|
||||
*
|
||||
* Example:
|
||||
* LoopContacts.search({
|
||||
* q: "foo@bar.com",
|
||||
* field: "email" // 'email' is the default.
|
||||
* }, function(err, contacts) {
|
||||
* if (err) {
|
||||
* throw err;
|
||||
* }
|
||||
* console.dir(contacts);
|
||||
* });
|
||||
*/
|
||||
search: function(query, callback) {
|
||||
//TODO in bug 1037114.
|
||||
callback(new Error("Not implemented yet!"));
|
||||
if (!("q" in query) || !query.q) {
|
||||
callback(new Error("Nothing to search for. 'q' is required."));
|
||||
return;
|
||||
}
|
||||
if (!("field" in query)) {
|
||||
query.field = "email";
|
||||
}
|
||||
let queryValue = query.q;
|
||||
if (query.field == "tel") {
|
||||
queryValue = queryValue.replace(/[\D]+/g, "");
|
||||
}
|
||||
|
||||
const checkForMatch = function(fieldValue) {
|
||||
if (typeof fieldValue == "string") {
|
||||
if (query.field == "tel") {
|
||||
return fieldValue.replace(/[\D]+/g, "").endsWith(queryValue);
|
||||
}
|
||||
return fieldValue == queryValue;
|
||||
}
|
||||
if (typeof fieldValue == "number" || typeof fieldValue == "boolean") {
|
||||
return fieldValue == queryValue;
|
||||
}
|
||||
if ("value" in fieldValue) {
|
||||
return checkForMatch(fieldValue.value);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
let foundContacts = [];
|
||||
this.getAll((err, contacts) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
for (let contact of contacts) {
|
||||
let matchWith = contact[query.field];
|
||||
if (!matchWith) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Many fields are defined as Arrays.
|
||||
if (Array.isArray(matchWith)) {
|
||||
for (let fieldValue of matchWith) {
|
||||
if (checkForMatch(fieldValue)) {
|
||||
foundContacts.push(contact);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (checkForMatch(matchWith)) {
|
||||
foundContacts.push(contact);
|
||||
}
|
||||
}
|
||||
|
||||
callback(null, foundContacts);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ const LOOP_SESSION_TYPE = {
|
|||
// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
|
||||
const PREF_LOG_LEVEL = "loop.debug.loglevel";
|
||||
|
||||
const EMAIL_OR_PHONE_RE = /^(:?\S+@\S+|\+\d+)$/;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
@ -56,6 +58,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "HawkClient",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "deriveHawkCredentials",
|
||||
"resource://services-common/hawkrequest.js");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LoopContacts",
|
||||
"resource:///modules/loop/LoopContacts.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LoopStorage",
|
||||
"resource:///modules/loop/LoopStorage.jsm");
|
||||
|
||||
|
@ -786,17 +791,46 @@ let MozLoopServiceInternal = {
|
|||
* Starts a call, saves the call data, and opens a chat window.
|
||||
*
|
||||
* @param {Object} callData The data associated with the call including an id.
|
||||
* @param {Boolean} conversationType Whether or not the call is "incoming"
|
||||
* or "outgoing"
|
||||
* @param {String} conversationType Whether or not the call is "incoming"
|
||||
* or "outgoing"
|
||||
*/
|
||||
_startCall: function(callData, conversationType) {
|
||||
this.callsData.inUse = true;
|
||||
this.callsData.data = callData;
|
||||
this.openChatWindow(
|
||||
null,
|
||||
// No title, let the page set that, to avoid flickering.
|
||||
"",
|
||||
"about:loopconversation#" + conversationType + "/" + callData.callId);
|
||||
const openChat = () => {
|
||||
this.callsData.inUse = true;
|
||||
this.callsData.data = callData;
|
||||
|
||||
this.openChatWindow(
|
||||
null,
|
||||
// No title, let the page set that, to avoid flickering.
|
||||
"",
|
||||
"about:loopconversation#" + conversationType + "/" + callData.callId);
|
||||
};
|
||||
|
||||
if (conversationType == "incoming" && ("callerId" in callData) &&
|
||||
EMAIL_OR_PHONE_RE.test(callData.callerId)) {
|
||||
LoopContacts.search({
|
||||
q: callData.callerId,
|
||||
field: callData.callerId.contains("@") ? "email" : "tel"
|
||||
}, (err, contacts) => {
|
||||
if (err) {
|
||||
// Database error, helas!
|
||||
openChat();
|
||||
return;
|
||||
}
|
||||
|
||||
for (let contact of contacts) {
|
||||
if (contact.blocked) {
|
||||
// Blocked! Send a busy signal back to the caller.
|
||||
this._returnBusy(callData);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
openChat();
|
||||
})
|
||||
} else {
|
||||
openChat();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -86,12 +86,15 @@ loop.contacts = (function(_, mozL10n) {
|
|||
return (
|
||||
React.DOM.ul({className: cx({ "dropdown-menu": true,
|
||||
"dropdown-menu-up": this.state.openDirUp })},
|
||||
React.DOM.li({className: cx({ "dropdown-menu-item": true }),
|
||||
onClick: this.onItemClick, 'data-action': "video-call"},
|
||||
React.DOM.li({className: cx({ "dropdown-menu-item": true,
|
||||
"disabled": this.props.blocked }),
|
||||
onClick: this.onItemClick,
|
||||
'data-action': "video-call"},
|
||||
React.DOM.i({className: "icon icon-video-call"}),
|
||||
mozL10n.get("video_call_menu_button")
|
||||
),
|
||||
React.DOM.li({className: cx({ "dropdown-menu-item": true }),
|
||||
React.DOM.li({className: cx({ "dropdown-menu-item": true,
|
||||
"disabled": this.props.blocked }),
|
||||
onClick: this.onItemClick, 'data-action': "audio-call"},
|
||||
React.DOM.i({className: "icon icon-audio-call"}),
|
||||
mozL10n.get("audio_call_menu_button")
|
||||
|
@ -388,10 +391,14 @@ loop.contacts = (function(_, mozL10n) {
|
|||
});
|
||||
break;
|
||||
case "video-call":
|
||||
navigator.mozLoop.startDirectCall(contact, CALL_TYPES.AUDIO_VIDEO);
|
||||
if (!contact.blocked) {
|
||||
navigator.mozLoop.startDirectCall(contact, CALL_TYPES.AUDIO_VIDEO);
|
||||
}
|
||||
break;
|
||||
case "audio-call":
|
||||
navigator.mozLoop.startDirectCall(contact, CALL_TYPES.AUDIO_ONLY);
|
||||
if (!contact.blocked) {
|
||||
navigator.mozLoop.startDirectCall(contact, CALL_TYPES.AUDIO_ONLY);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.error("Unrecognized action: " + actionName);
|
||||
|
|
|
@ -86,12 +86,15 @@ loop.contacts = (function(_, mozL10n) {
|
|||
return (
|
||||
<ul className={cx({ "dropdown-menu": true,
|
||||
"dropdown-menu-up": this.state.openDirUp })}>
|
||||
<li className={cx({ "dropdown-menu-item": true })}
|
||||
onClick={this.onItemClick} data-action="video-call">
|
||||
<li className={cx({ "dropdown-menu-item": true,
|
||||
"disabled": this.props.blocked })}
|
||||
onClick={this.onItemClick}
|
||||
data-action="video-call">
|
||||
<i className="icon icon-video-call" />
|
||||
{mozL10n.get("video_call_menu_button")}
|
||||
</li>
|
||||
<li className={cx({ "dropdown-menu-item": true })}
|
||||
<li className={cx({ "dropdown-menu-item": true,
|
||||
"disabled": this.props.blocked })}
|
||||
onClick={this.onItemClick} data-action="audio-call">
|
||||
<i className="icon icon-audio-call" />
|
||||
{mozL10n.get("audio_call_menu_button")}
|
||||
|
@ -388,10 +391,14 @@ loop.contacts = (function(_, mozL10n) {
|
|||
});
|
||||
break;
|
||||
case "video-call":
|
||||
navigator.mozLoop.startDirectCall(contact, CALL_TYPES.AUDIO_VIDEO);
|
||||
if (!contact.blocked) {
|
||||
navigator.mozLoop.startDirectCall(contact, CALL_TYPES.AUDIO_VIDEO);
|
||||
}
|
||||
break;
|
||||
case "audio-call":
|
||||
navigator.mozLoop.startDirectCall(contact, CALL_TYPES.AUDIO_ONLY);
|
||||
if (!contact.blocked) {
|
||||
navigator.mozLoop.startDirectCall(contact, CALL_TYPES.AUDIO_ONLY);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.error("Unrecognized action: " + actionName);
|
||||
|
|
|
@ -406,6 +406,12 @@ loop.store.ConversationStore = (function() {
|
|||
this._websocket.close();
|
||||
delete this._websocket;
|
||||
}
|
||||
|
||||
// XXX: The internal callId is different from
|
||||
// this.get("callId"), see bug 1084228 for more info.
|
||||
var locationHash = new loop.shared.utils.Helper().locationHash();
|
||||
var callId = locationHash.match(/\#outgoing\/(.*)/)[1];
|
||||
navigator.mozLoop.releaseCallData(callId);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,19 +17,25 @@ function promiseImport() {
|
|||
});
|
||||
}
|
||||
|
||||
const kContactsCount = 7;
|
||||
|
||||
add_task(function* test_GoogleImport() {
|
||||
let stats;
|
||||
// An error may throw and the test will fail when that happens.
|
||||
stats = yield promiseImport();
|
||||
|
||||
let contactsCount = mockDb.size;
|
||||
|
||||
// Assert the world.
|
||||
Assert.equal(stats.total, 6, "Five contacts should get processed");
|
||||
Assert.equal(stats.success, 6, "Five contacts should be imported");
|
||||
Assert.equal(stats.total, contactsCount, "Five contacts should get processed");
|
||||
Assert.equal(stats.success, contactsCount, "Five contacts should be imported");
|
||||
|
||||
yield promiseImport();
|
||||
Assert.equal(Object.keys(mockDb._store).length, 6, "Database should contain only five contact after reimport");
|
||||
Assert.equal(Object.keys(mockDb._store).length, contactsCount, "Database should be the same size after reimport");
|
||||
|
||||
let c = mockDb._store[mockDb._next_guid - 6];
|
||||
let currentContact = contactsCount;
|
||||
|
||||
let c = mockDb._store[mockDb._next_guid - currentContact];
|
||||
Assert.equal(c.name[0], "John Smith", "Full name should match");
|
||||
Assert.equal(c.givenName[0], "John", "Given name should match");
|
||||
Assert.equal(c.familyName[0], "Smith", "Family name should match");
|
||||
|
@ -39,7 +45,7 @@ add_task(function* test_GoogleImport() {
|
|||
Assert.equal(c.category[0], "google", "Category should match");
|
||||
Assert.equal(c.id, "http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/0", "UID should match and be scoped to provider");
|
||||
|
||||
c = mockDb._store[mockDb._next_guid - 5];
|
||||
c = mockDb._store[mockDb._next_guid - (--currentContact)];
|
||||
Assert.equal(c.name[0], "Jane Smith", "Full name should match");
|
||||
Assert.equal(c.givenName[0], "Jane", "Given name should match");
|
||||
Assert.equal(c.familyName[0], "Smith", "Family name should match");
|
||||
|
@ -49,7 +55,7 @@ add_task(function* test_GoogleImport() {
|
|||
Assert.equal(c.category[0], "google", "Category should match");
|
||||
Assert.equal(c.id, "http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/1", "UID should match and be scoped to provider");
|
||||
|
||||
c = mockDb._store[mockDb._next_guid - 4];
|
||||
c = mockDb._store[mockDb._next_guid - (--currentContact)];
|
||||
Assert.equal(c.name[0], "Davy Randall Jones", "Full name should match");
|
||||
Assert.equal(c.givenName[0], "Davy Randall", "Given name should match");
|
||||
Assert.equal(c.familyName[0], "Jones", "Family name should match");
|
||||
|
@ -59,7 +65,7 @@ add_task(function* test_GoogleImport() {
|
|||
Assert.equal(c.category[0], "google", "Category should match");
|
||||
Assert.equal(c.id, "http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/2", "UID should match and be scoped to provider");
|
||||
|
||||
c = mockDb._store[mockDb._next_guid - 3];
|
||||
c = mockDb._store[mockDb._next_guid - (--currentContact)];
|
||||
Assert.equal(c.name[0], "noname@example.com", "Full name should match");
|
||||
Assert.equal(c.email[0].type, "other", "Email type should match");
|
||||
Assert.equal(c.email[0].value, "noname@example.com", "Email should match");
|
||||
|
@ -67,7 +73,7 @@ add_task(function* test_GoogleImport() {
|
|||
Assert.equal(c.category[0], "google", "Category should match");
|
||||
Assert.equal(c.id, "http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/3", "UID should match and be scoped to provider");
|
||||
|
||||
c = mockDb._store[mockDb._next_guid - 2];
|
||||
c = mockDb._store[mockDb._next_guid - (--currentContact)];
|
||||
Assert.equal(c.name[0], "lycnix", "Full name should match");
|
||||
Assert.equal(c.email[0].type, "other", "Email type should match");
|
||||
Assert.equal(c.email[0].value, "lycnix", "Email should match");
|
||||
|
@ -75,11 +81,19 @@ add_task(function* test_GoogleImport() {
|
|||
Assert.equal(c.category[0], "google", "Category should match");
|
||||
Assert.equal(c.id, "http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/7", "UID should match and be scoped to provider");
|
||||
|
||||
c = mockDb._store[mockDb._next_guid - 1];
|
||||
c = mockDb._store[mockDb._next_guid - (--currentContact)];
|
||||
Assert.equal(c.name[0], "+31-6-12345678", "Full name should match");
|
||||
Assert.equal(c.tel[0].type, "mobile", "Email type should match");
|
||||
Assert.equal(c.tel[0].value, "+31-6-12345678", "Email should match");
|
||||
Assert.equal(c.tel[0].type, "mobile", "Phone type should match");
|
||||
Assert.equal(c.tel[0].value, "+31-6-12345678", "Phone should match");
|
||||
Assert.equal(c.tel[0].pref, false, "Pref should match");
|
||||
Assert.equal(c.category[0], "google", "Category should match");
|
||||
Assert.equal(c.id, "http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/8", "UID should match and be scoped to provider");
|
||||
|
||||
c = mockDb._store[mockDb._next_guid - (--currentContact)];
|
||||
Assert.equal(c.name[0], "215234523452345", "Full name should match");
|
||||
Assert.equal(c.tel[0].type, "mobile", "Phone type should match");
|
||||
Assert.equal(c.tel[0].value, "215234523452345", "Phone should match");
|
||||
Assert.equal(c.tel[0].pref, false, "Pref should match");
|
||||
Assert.equal(c.category[0], "google", "Category should match");
|
||||
Assert.equal(c.id, "http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/6", "UID should match and be scoped to provider");
|
||||
});
|
||||
|
|
|
@ -16,6 +16,11 @@ const kContacts = [{
|
|||
"type": ["work"],
|
||||
"value": "ally@mail.com"
|
||||
}],
|
||||
tel: [{
|
||||
"pref": true,
|
||||
"type": ["mobile"],
|
||||
"value": "+31-6-12345678"
|
||||
}],
|
||||
category: ["google"],
|
||||
published: 1406798311748,
|
||||
updated: 1406798311748
|
||||
|
@ -27,6 +32,11 @@ const kContacts = [{
|
|||
"type": ["work"],
|
||||
"value": "bob@gmail.com"
|
||||
}],
|
||||
tel: [{
|
||||
"pref": true,
|
||||
"type": ["mobile"],
|
||||
"value": "+1-214-5551234"
|
||||
}],
|
||||
category: ["local"],
|
||||
published: 1406798311748,
|
||||
updated: 1406798311748
|
||||
|
@ -425,11 +435,50 @@ add_task(function* () {
|
|||
LoopStorage.switchDatabase();
|
||||
Assert.equal(LoopStorage.databaseName, "default", "The active partition should have changed");
|
||||
|
||||
LoopContacts.getAll(function(err, contacts) {
|
||||
Assert.equal(err, null, "There shouldn't be an error");
|
||||
|
||||
for (let i = 0, l = contacts.length; i < l; ++i) {
|
||||
compareContacts(contacts[i], kContacts[i]);
|
||||
}
|
||||
});
|
||||
contacts = yield LoopContacts.promise("getAll");
|
||||
for (let i = 0, l = contacts.length; i < l; ++i) {
|
||||
compareContacts(contacts[i], kContacts[i]);
|
||||
}
|
||||
});
|
||||
|
||||
// Test searching for contacts.
|
||||
add_task(function* () {
|
||||
yield promiseLoadContacts();
|
||||
|
||||
let contacts = yield LoopContacts.promise("search", {
|
||||
q: "bob@gmail.com"
|
||||
});
|
||||
Assert.equal(contacts.length, 1, "There should be one contact found");
|
||||
compareContacts(contacts[0], kContacts[1]);
|
||||
|
||||
// Test searching by name.
|
||||
contacts = yield LoopContacts.promise("search", {
|
||||
q: "Ally Avocado",
|
||||
field: "name"
|
||||
});
|
||||
Assert.equal(contacts.length, 1, "There should be one contact found");
|
||||
compareContacts(contacts[0], kContacts[0]);
|
||||
|
||||
// Test searching for multiple contacts.
|
||||
contacts = yield LoopContacts.promise("search", {
|
||||
q: "google",
|
||||
field: "category"
|
||||
});
|
||||
Assert.equal(contacts.length, 2, "There should be two contacts found");
|
||||
|
||||
// Test searching for telephone numbers.
|
||||
contacts = yield LoopContacts.promise("search", {
|
||||
q: "+31612345678",
|
||||
field: "tel"
|
||||
});
|
||||
Assert.equal(contacts.length, 1, "There should be one contact found");
|
||||
compareContacts(contacts[0], kContacts[0]);
|
||||
|
||||
// Test searching for telephone numbers without prefixes.
|
||||
contacts = yield LoopContacts.promise("search", {
|
||||
q: "5551234",
|
||||
field: "tel"
|
||||
});
|
||||
Assert.equal(contacts.length, 1, "There should be one contact found");
|
||||
compareContacts(contacts[0], kContacts[1]);
|
||||
});
|
||||
|
|
|
@ -102,4 +102,16 @@
|
|||
<link href="https://www.google.com/m8/feeds/contacts/tester%40mochi.com/full/8" rel="edit" type="application/atom+xml"/>
|
||||
<gd:phoneNumber rel="http://schemas.google.com/g/2005#mobile" uri="tel:+31-6-12345678">0612345678</gd:phoneNumber>
|
||||
</entry>
|
||||
<entry gd:etag=""SX8-ejVSLit7I2A9XRdQFUkDRgY."">
|
||||
<id>http://www.google.com/m8/feeds/contacts/tester%40mochi.com/base/6</id>
|
||||
<updated>2014-10-17T12:32:08.152Z</updated>
|
||||
<app:edited xmlns:app="http://www.w3.org/2007/app">2014-10-17T12:32:08.152Z</app:edited>
|
||||
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact"/>
|
||||
<title/>
|
||||
<link href="https://www.google.com/m8/feeds/photos/media/tester%40mochi.com/6" rel="http://schemas.google.com/contacts/2008/rel#photo" type="image/*"/>
|
||||
<link href="https://www.google.com/m8/feeds/contacts/tester%40mochi.com/full/6" rel="self" type="application/atom+xml"/>
|
||||
<link href="https://www.google.com/m8/feeds/contacts/tester%40mochi.com/full/6" rel="edit" type="application/atom+xml"/>
|
||||
<gd:phoneNumber rel="http://schemas.google.com/g/2005#mobile">215234523452345</gd:phoneNumber>
|
||||
<gContact:groupMembershipInfo deleted="false" href="http://www.google.com/m8/feeds/groups/tester%40mochi.com/base/6"/>
|
||||
</entry>
|
||||
</feed>
|
||||
|
|
|
@ -218,6 +218,10 @@ const mockDb = {
|
|||
_store: { },
|
||||
_next_guid: 1,
|
||||
|
||||
get size() {
|
||||
return Object.getOwnPropertyNames(this._store).length;
|
||||
},
|
||||
|
||||
add: function(details, callback) {
|
||||
if (!("id" in details)) {
|
||||
callback(new Error("No 'id' field present"));
|
||||
|
|
|
@ -36,6 +36,11 @@ describe("loop.store.ConversationStore", function () {
|
|||
}]
|
||||
};
|
||||
|
||||
navigator.mozLoop = {
|
||||
getLoopBoolPref: sandbox.stub(),
|
||||
releaseCallData: sandbox.stub()
|
||||
};
|
||||
|
||||
dispatcher = new loop.Dispatcher();
|
||||
client = {
|
||||
setupOutgoingCall: sinon.stub(),
|
||||
|
@ -120,6 +125,8 @@ describe("loop.store.ConversationStore", function () {
|
|||
describe("#connectionFailure", function() {
|
||||
beforeEach(function() {
|
||||
store._websocket = fakeWebsocket;
|
||||
sandbox.stub(loop.shared.utils.Helper.prototype, "locationHash")
|
||||
.returns("#outgoing/42");
|
||||
});
|
||||
|
||||
it("should disconnect the session", function() {
|
||||
|
@ -145,6 +152,14 @@ describe("loop.store.ConversationStore", function () {
|
|||
expect(store.get("callState")).eql(CALL_STATES.TERMINATED);
|
||||
expect(store.get("callStateReason")).eql("fake");
|
||||
});
|
||||
|
||||
it("should release mozLoop callsData", function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.ConnectionFailure({reason: "fake"}));
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.releaseCallData);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.releaseCallData, "42");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#connectionProgress", function() {
|
||||
|
@ -481,6 +496,8 @@ describe("loop.store.ConversationStore", function () {
|
|||
close: wsCloseSpy
|
||||
};
|
||||
store.set({callState: CALL_STATES.ONGOING});
|
||||
sandbox.stub(loop.shared.utils.Helper.prototype, "locationHash")
|
||||
.returns("#outgoing/42");
|
||||
});
|
||||
|
||||
it("should disconnect the session", function() {
|
||||
|
@ -506,6 +523,13 @@ describe("loop.store.ConversationStore", function () {
|
|||
|
||||
expect(store.get("callState")).eql(CALL_STATES.FINISHED);
|
||||
});
|
||||
|
||||
it("should release mozLoop callsData", function() {
|
||||
dispatcher.dispatch(new sharedActions.HangupCall());
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.releaseCallData);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.releaseCallData, "42");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#peerHungupCall", function() {
|
||||
|
@ -519,6 +543,8 @@ describe("loop.store.ConversationStore", function () {
|
|||
close: wsCloseSpy
|
||||
};
|
||||
store.set({callState: CALL_STATES.ONGOING});
|
||||
sandbox.stub(loop.shared.utils.Helper.prototype, "locationHash")
|
||||
.returns("#outgoing/42");
|
||||
});
|
||||
|
||||
it("should disconnect the session", function() {
|
||||
|
@ -538,6 +564,13 @@ describe("loop.store.ConversationStore", function () {
|
|||
|
||||
expect(store.get("callState")).eql(CALL_STATES.FINISHED);
|
||||
});
|
||||
|
||||
it("should release mozLoop callsData", function() {
|
||||
dispatcher.dispatch(new sharedActions.PeerHungupCall());
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.releaseCallData);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.releaseCallData, "42");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#cancelCall", function() {
|
||||
|
@ -545,6 +578,8 @@ describe("loop.store.ConversationStore", function () {
|
|||
store._websocket = fakeWebsocket;
|
||||
|
||||
store.set({callState: CALL_STATES.CONNECTING});
|
||||
sandbox.stub(loop.shared.utils.Helper.prototype, "locationHash")
|
||||
.returns("#outgoing/42");
|
||||
});
|
||||
|
||||
it("should disconnect the session", function() {
|
||||
|
@ -579,6 +614,12 @@ describe("loop.store.ConversationStore", function () {
|
|||
expect(store.get("callState")).eql(CALL_STATES.CLOSE);
|
||||
});
|
||||
|
||||
it("should release mozLoop callsData", function() {
|
||||
dispatcher.dispatch(new sharedActions.CancelCall());
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.releaseCallData);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.releaseCallData, "42");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#retryCall", function() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: 1.0.801
|
||||
Current extension version is: 1.0.907
|
||||
|
||||
|
|
|
@ -78,7 +78,9 @@ var DEFAULT_PREFERENCES = {
|
|||
sidebarViewOnLoad: 0,
|
||||
enableHandToolOnLoad: false,
|
||||
enableWebGL: false,
|
||||
pdfBugEnabled: false,
|
||||
disableRange: false,
|
||||
disableStream: false,
|
||||
disableAutoFetch: false,
|
||||
disableFontFace: false,
|
||||
disableTextLayer: false,
|
||||
|
|
|
@ -77,7 +77,8 @@ function getFindBar(domWindow) {
|
|||
var browser = getContainingBrowser(domWindow);
|
||||
try {
|
||||
var tabbrowser = browser.getTabBrowser();
|
||||
var tab = tabbrowser.getTabForBrowser(browser);
|
||||
var tab;
|
||||
tab = tabbrowser.getTabForBrowser(browser);
|
||||
return tabbrowser.getFindBar(tab);
|
||||
} catch (e) {
|
||||
// FF22 has no _getTabForBrowser, and FF24 has no getFindBar
|
||||
|
@ -162,40 +163,38 @@ function makeContentReadable(obj, window) {
|
|||
// PDF data storage
|
||||
function PdfDataListener(length) {
|
||||
this.length = length; // less than 0, if length is unknown
|
||||
this.data = new Uint8Array(length >= 0 ? length : 0x10000);
|
||||
this.buffer = null;
|
||||
this.loaded = 0;
|
||||
}
|
||||
|
||||
PdfDataListener.prototype = {
|
||||
append: function PdfDataListener_append(chunk) {
|
||||
var willBeLoaded = this.loaded + chunk.length;
|
||||
if (this.length >= 0 && this.length < willBeLoaded) {
|
||||
// In most of the cases we will pass data as we receive it, but at the
|
||||
// beginning of the loading we may accumulate some data.
|
||||
if (!this.buffer) {
|
||||
this.buffer = new Uint8Array(chunk);
|
||||
} else {
|
||||
var buffer = this.buffer;
|
||||
var newBuffer = new Uint8Array(buffer.length + chunk.length);
|
||||
newBuffer.set(buffer);
|
||||
newBuffer.set(chunk, buffer.length);
|
||||
this.buffer = newBuffer;
|
||||
}
|
||||
this.loaded += chunk.length;
|
||||
if (this.length >= 0 && this.length < this.loaded) {
|
||||
this.length = -1; // reset the length, server is giving incorrect one
|
||||
}
|
||||
if (this.length < 0 && this.data.length < willBeLoaded) {
|
||||
// data length is unknown and new chunk will not fit in the existing
|
||||
// buffer, resizing the buffer by doubling the its last length
|
||||
var newLength = this.data.length;
|
||||
for (; newLength < willBeLoaded; newLength *= 2) {}
|
||||
var newData = new Uint8Array(newLength);
|
||||
newData.set(this.data);
|
||||
this.data = newData;
|
||||
}
|
||||
this.data.set(chunk, this.loaded);
|
||||
this.loaded = willBeLoaded;
|
||||
this.onprogress(this.loaded, this.length >= 0 ? this.length : void(0));
|
||||
},
|
||||
getData: function PdfDataListener_getData() {
|
||||
var data = this.data;
|
||||
if (this.loaded != data.length)
|
||||
data = data.subarray(0, this.loaded);
|
||||
delete this.data; // releasing temporary storage
|
||||
return data;
|
||||
readData: function PdfDataListener_readData() {
|
||||
var result = this.buffer;
|
||||
this.buffer = null;
|
||||
return result;
|
||||
},
|
||||
finish: function PdfDataListener_finish() {
|
||||
this.isDataReady = true;
|
||||
if (this.oncompleteCallback) {
|
||||
this.oncompleteCallback(this.getData());
|
||||
this.oncompleteCallback(this.readData());
|
||||
}
|
||||
},
|
||||
error: function PdfDataListener_error(errorCode) {
|
||||
|
@ -211,7 +210,7 @@ PdfDataListener.prototype = {
|
|||
set oncomplete(value) {
|
||||
this.oncompleteCallback = value;
|
||||
if (this.isDataReady) {
|
||||
value(this.getData());
|
||||
value(this.readData());
|
||||
}
|
||||
if (this.errorCode) {
|
||||
value(null, this.errorCode);
|
||||
|
@ -234,7 +233,7 @@ function ChromeActions(domWindow, contentDispositionFilename) {
|
|||
|
||||
ChromeActions.prototype = {
|
||||
isInPrivateBrowsing: function() {
|
||||
return PrivateBrowsingUtils.isWindowPrivate(this.domWindow);
|
||||
return PrivateBrowsingUtils.isContentWindowPrivate(this.domWindow);
|
||||
},
|
||||
download: function(data, sendResponse) {
|
||||
var self = this;
|
||||
|
@ -329,9 +328,6 @@ ChromeActions.prototype = {
|
|||
return 'null';
|
||||
}
|
||||
},
|
||||
pdfBugEnabled: function() {
|
||||
return getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false);
|
||||
},
|
||||
supportsIntegratedFind: function() {
|
||||
// Integrated find is only supported when we're not in a frame
|
||||
if (this.domWindow.frameElement !== null) {
|
||||
|
@ -511,11 +507,13 @@ var RangedChromeActions = (function RangedChromeActionsClosure() {
|
|||
*/
|
||||
function RangedChromeActions(
|
||||
domWindow, contentDispositionFilename, originalRequest,
|
||||
dataListener) {
|
||||
rangeEnabled, streamingEnabled, dataListener) {
|
||||
|
||||
ChromeActions.call(this, domWindow, contentDispositionFilename);
|
||||
this.dataListener = dataListener;
|
||||
this.originalRequest = originalRequest;
|
||||
this.rangeEnabled = rangeEnabled;
|
||||
this.streamingEnabled = streamingEnabled;
|
||||
|
||||
this.pdfUrl = originalRequest.URI.spec;
|
||||
this.contentLength = originalRequest.contentLength;
|
||||
|
@ -534,7 +532,9 @@ var RangedChromeActions = (function RangedChromeActionsClosure() {
|
|||
this.headers[aHeader] = aValue;
|
||||
}
|
||||
};
|
||||
originalRequest.visitRequestHeaders(httpHeaderVisitor);
|
||||
if (originalRequest.visitRequestHeaders) {
|
||||
originalRequest.visitRequestHeaders(httpHeaderVisitor);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var xhr_onreadystatechange = function xhr_onreadystatechange() {
|
||||
|
@ -573,20 +573,46 @@ var RangedChromeActions = (function RangedChromeActionsClosure() {
|
|||
proto.constructor = RangedChromeActions;
|
||||
|
||||
proto.initPassiveLoading = function RangedChromeActions_initPassiveLoading() {
|
||||
this.originalRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
this.originalRequest = null;
|
||||
var self = this;
|
||||
var data;
|
||||
if (!this.streamingEnabled) {
|
||||
this.originalRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
this.originalRequest = null;
|
||||
data = this.dataListener.readData();
|
||||
this.dataListener = null;
|
||||
} else {
|
||||
data = this.dataListener.readData();
|
||||
|
||||
this.dataListener.onprogress = function (loaded, total) {
|
||||
self.domWindow.postMessage({
|
||||
pdfjsLoadAction: 'progressiveRead',
|
||||
loaded: loaded,
|
||||
total: total,
|
||||
chunk: self.dataListener.readData()
|
||||
}, '*');
|
||||
};
|
||||
this.dataListener.oncomplete = function () {
|
||||
delete self.dataListener;
|
||||
};
|
||||
}
|
||||
|
||||
this.domWindow.postMessage({
|
||||
pdfjsLoadAction: 'supportsRangedLoading',
|
||||
rangeEnabled: this.rangeEnabled,
|
||||
streamingEnabled: this.streamingEnabled,
|
||||
pdfUrl: this.pdfUrl,
|
||||
length: this.contentLength,
|
||||
data: this.dataListener.getData()
|
||||
data: data
|
||||
}, '*');
|
||||
this.dataListener = null;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
proto.requestDataRange = function RangedChromeActions_requestDataRange(args) {
|
||||
if (!this.rangeEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
var begin = args.begin;
|
||||
var end = args.end;
|
||||
var domWindow = this.domWindow;
|
||||
|
@ -828,6 +854,7 @@ PdfStreamConverter.prototype = {
|
|||
} catch (e) {}
|
||||
|
||||
var rangeRequest = false;
|
||||
var streamRequest = false;
|
||||
if (isHttpRequest) {
|
||||
var contentEncoding = 'identity';
|
||||
try {
|
||||
|
@ -840,10 +867,18 @@ PdfStreamConverter.prototype = {
|
|||
} catch (e) {}
|
||||
|
||||
var hash = aRequest.URI.ref;
|
||||
var isPDFBugEnabled = getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false);
|
||||
rangeRequest = contentEncoding === 'identity' &&
|
||||
acceptRanges === 'bytes' &&
|
||||
aRequest.contentLength >= 0 &&
|
||||
hash.indexOf('disableRange=true') < 0;
|
||||
!getBoolPref(PREF_PREFIX + '.disableRange', false) &&
|
||||
(!isPDFBugEnabled ||
|
||||
hash.toLowerCase().indexOf('disablerange=true') < 0);
|
||||
streamRequest = contentEncoding === 'identity' &&
|
||||
aRequest.contentLength >= 0 &&
|
||||
!getBoolPref(PREF_PREFIX + '.disableStream', false) &&
|
||||
(!isPDFBugEnabled ||
|
||||
hash.toLowerCase().indexOf('disablestream=true') < 0);
|
||||
}
|
||||
|
||||
aRequest.QueryInterface(Ci.nsIChannel);
|
||||
|
@ -897,12 +932,13 @@ PdfStreamConverter.prototype = {
|
|||
// may have changed during a redirect.
|
||||
var domWindow = getDOMWindow(channel);
|
||||
var actions;
|
||||
if (rangeRequest) {
|
||||
if (rangeRequest || streamRequest) {
|
||||
actions = new RangedChromeActions(
|
||||
domWindow, contentDispositionFilename, aRequest, dataListener);
|
||||
domWindow, contentDispositionFilename, aRequest,
|
||||
rangeRequest, streamRequest, dataListener);
|
||||
} else {
|
||||
actions = new StandardChromeActions(
|
||||
domWindow, contentDispositionFilename, dataListener);
|
||||
domWindow, contentDispositionFilename, dataListener);
|
||||
}
|
||||
var requestListener = new RequestListener(actions);
|
||||
domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {
|
||||
|
|
|
@ -43,7 +43,9 @@ var DEFAULT_PREFERENCES = {
|
|||
sidebarViewOnLoad: 0,
|
||||
enableHandToolOnLoad: false,
|
||||
enableWebGL: false,
|
||||
pdfBugEnabled: false,
|
||||
disableRange: false,
|
||||
disableStream: false,
|
||||
disableAutoFetch: false,
|
||||
disableFontFace: false,
|
||||
disableTextLayer: false,
|
||||
|
@ -285,7 +287,8 @@ let PdfjsChromeUtils = {
|
|||
*/
|
||||
function PdfjsFindbarWrapper(aBrowser) {
|
||||
let tabbrowser = aBrowser.getTabBrowser();
|
||||
let tab = tabbrowser.getTabForBrowser(aBrowser);
|
||||
let tab;
|
||||
tab = tabbrowser.getTabForBrowser(aBrowser);
|
||||
this._findbar = tabbrowser.getFindBar(tab);
|
||||
};
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') {
|
|||
(typeof window !== 'undefined' ? window : this).PDFJS = {};
|
||||
}
|
||||
|
||||
PDFJS.version = '1.0.801';
|
||||
PDFJS.build = 'e77e5c4';
|
||||
PDFJS.version = '1.0.907';
|
||||
PDFJS.build = 'e9072ac';
|
||||
|
||||
(function pdfjsWrapper() {
|
||||
// Use strict in our context only - users might not want it
|
||||
|
@ -1362,6 +1362,14 @@ PDFJS.workerSrc = (PDFJS.workerSrc === undefined ? null : PDFJS.workerSrc);
|
|||
PDFJS.disableRange = (PDFJS.disableRange === undefined ?
|
||||
false : PDFJS.disableRange);
|
||||
|
||||
/**
|
||||
* Disable streaming of PDF file data. By default PDF.js attempts to load PDF
|
||||
* in chunks. This default behavior can be disabled.
|
||||
* @var {boolean}
|
||||
*/
|
||||
PDFJS.disableStream = (PDFJS.disableStream === undefined ?
|
||||
false : PDFJS.disableStream);
|
||||
|
||||
/**
|
||||
* Disable pre-fetching of PDF file data. When range requests are enabled PDF.js
|
||||
* will automatically keep fetching more data even if it isn't needed to display
|
||||
|
@ -1560,10 +1568,20 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
|
|||
/**
|
||||
* @return {Promise} A promise that is resolved with a lookup table for
|
||||
* mapping named destinations to reference numbers.
|
||||
*
|
||||
* This can be slow for large documents: use getDestination instead
|
||||
*/
|
||||
getDestinations: function PDFDocumentProxy_getDestinations() {
|
||||
return this.transport.getDestinations();
|
||||
},
|
||||
/**
|
||||
* @param {string} id The named destination to get.
|
||||
* @return {Promise} A promise that is resolved with all information
|
||||
* of the given named destination.
|
||||
*/
|
||||
getDestination: function PDFDocumentProxy_getDestination(id) {
|
||||
return this.transport.getDestination(id);
|
||||
},
|
||||
/**
|
||||
* @return {Promise} A promise that is resolved with a lookup table for
|
||||
* mapping named attachments to their content.
|
||||
|
@ -2117,6 +2135,12 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||
});
|
||||
});
|
||||
|
||||
pdfDataRangeTransport.addProgressiveReadListener(function(chunk) {
|
||||
messageHandler.send('OnDataRange', {
|
||||
chunk: chunk
|
||||
});
|
||||
});
|
||||
|
||||
messageHandler.on('RequestDataRange',
|
||||
function transportDataRange(data) {
|
||||
pdfDataRangeTransport.requestDataRange(data.begin, data.end);
|
||||
|
@ -2177,6 +2201,12 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||
this.downloadInfoCapability.resolve(data);
|
||||
}, this);
|
||||
|
||||
messageHandler.on('PDFManagerReady', function transportPage(data) {
|
||||
if (this.pdfDataRangeTransport) {
|
||||
this.pdfDataRangeTransport.transportReady();
|
||||
}
|
||||
}, this);
|
||||
|
||||
messageHandler.on('StartRenderPage', function transportRender(data) {
|
||||
var page = this.pageCache[data.pageIndex];
|
||||
|
||||
|
@ -2208,7 +2238,7 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||
this.commonObjs.resolve(id, error);
|
||||
break;
|
||||
} else {
|
||||
font = new FontFace(exportedData);
|
||||
font = new FontFaceObject(exportedData);
|
||||
}
|
||||
|
||||
FontLoader.bind(
|
||||
|
@ -2321,6 +2351,7 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||
|
||||
fetchDocument: function WorkerTransport_fetchDocument(source) {
|
||||
source.disableAutoFetch = PDFJS.disableAutoFetch;
|
||||
source.disableStream = PDFJS.disableStream;
|
||||
source.chunkedViewerLoading = !!this.pdfDataRangeTransport;
|
||||
this.messageHandler.send('GetDocRequest', {
|
||||
source: source,
|
||||
|
@ -2372,6 +2403,10 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||
return this.messageHandler.sendWithPromise('GetDestinations', null);
|
||||
},
|
||||
|
||||
getDestination: function WorkerTransport_getDestination(id) {
|
||||
return this.messageHandler.sendWithPromise('GetDestination', { id: id } );
|
||||
},
|
||||
|
||||
getAttachments: function WorkerTransport_getAttachments() {
|
||||
return this.messageHandler.sendWithPromise('GetAttachments', null);
|
||||
},
|
||||
|
@ -5731,8 +5766,8 @@ var FontLoader = {
|
|||
}
|
||||
};
|
||||
|
||||
var FontFace = (function FontFaceClosure() {
|
||||
function FontFace(name, file, properties) {
|
||||
var FontFaceObject = (function FontFaceObjectClosure() {
|
||||
function FontFaceObject(name, file, properties) {
|
||||
this.compiledGlyphs = {};
|
||||
if (arguments.length === 1) {
|
||||
// importing translated data
|
||||
|
@ -5743,8 +5778,9 @@ var FontFace = (function FontFaceClosure() {
|
|||
return;
|
||||
}
|
||||
}
|
||||
FontFace.prototype = {
|
||||
bindDOM: function FontFace_bindDOM() {
|
||||
FontFaceObject.prototype = {
|
||||
|
||||
bindDOM: function FontFaceObject_bindDOM() {
|
||||
if (!this.data) {
|
||||
return null;
|
||||
}
|
||||
|
@ -5771,7 +5807,7 @@ var FontFace = (function FontFaceClosure() {
|
|||
return rule;
|
||||
},
|
||||
|
||||
getPathGenerator: function (objs, character) {
|
||||
getPathGenerator: function FontLoader_getPathGenerator(objs, character) {
|
||||
if (!(character in this.compiledGlyphs)) {
|
||||
var js = objs.get(this.loadedName + '_path_' + character);
|
||||
/*jshint -W054 */
|
||||
|
@ -5780,7 +5816,7 @@ var FontFace = (function FontFaceClosure() {
|
|||
return this.compiledGlyphs[character];
|
||||
}
|
||||
};
|
||||
return FontFace;
|
||||
return FontFaceObject;
|
||||
})();
|
||||
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') {
|
|||
(typeof window !== 'undefined' ? window : this).PDFJS = {};
|
||||
}
|
||||
|
||||
PDFJS.version = '1.0.801';
|
||||
PDFJS.build = 'e77e5c4';
|
||||
PDFJS.version = '1.0.907';
|
||||
PDFJS.build = 'e9072ac';
|
||||
|
||||
(function pdfjsWrapper() {
|
||||
// Use strict in our context only - users might not want it
|
||||
|
@ -1309,7 +1309,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||
this.numChunksLoaded = 0;
|
||||
this.numChunks = Math.ceil(length / chunkSize);
|
||||
this.manager = manager;
|
||||
this.initialDataLength = 0;
|
||||
this.progressiveDataLength = 0;
|
||||
this.lastSuccessfulEnsureByteChunk = -1; // a single-entry cache
|
||||
}
|
||||
|
||||
|
@ -1320,7 +1320,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||
getMissingChunks: function ChunkedStream_getMissingChunks() {
|
||||
var chunks = [];
|
||||
for (var chunk = 0, n = this.numChunks; chunk < n; ++chunk) {
|
||||
if (!(chunk in this.loadedChunks)) {
|
||||
if (!this.loadedChunks[chunk]) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
}
|
||||
|
@ -1352,21 +1352,29 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||
var curChunk;
|
||||
|
||||
for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
|
||||
if (!(curChunk in this.loadedChunks)) {
|
||||
if (!this.loadedChunks[curChunk]) {
|
||||
this.loadedChunks[curChunk] = true;
|
||||
++this.numChunksLoaded;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onReceiveInitialData: function ChunkedStream_onReceiveInitialData(data) {
|
||||
this.bytes.set(data);
|
||||
this.initialDataLength = data.length;
|
||||
var endChunk = (this.end === data.length ?
|
||||
this.numChunks : Math.floor(data.length / this.chunkSize));
|
||||
for (var i = 0; i < endChunk; i++) {
|
||||
this.loadedChunks[i] = true;
|
||||
++this.numChunksLoaded;
|
||||
onReceiveProgressiveData:
|
||||
function ChunkedStream_onReceiveProgressiveData(data) {
|
||||
var position = this.progressiveDataLength;
|
||||
var beginChunk = Math.floor(position / this.chunkSize);
|
||||
|
||||
this.bytes.set(new Uint8Array(data), position);
|
||||
position += data.byteLength;
|
||||
this.progressiveDataLength = position;
|
||||
var endChunk = position >= this.end ? this.numChunks :
|
||||
Math.floor(position / this.chunkSize);
|
||||
var curChunk;
|
||||
for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
|
||||
if (!this.loadedChunks[curChunk]) {
|
||||
this.loadedChunks[curChunk] = true;
|
||||
++this.numChunksLoaded;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1376,7 +1384,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!(chunk in this.loadedChunks)) {
|
||||
if (!this.loadedChunks[chunk]) {
|
||||
throw new MissingDataException(pos, pos + 1);
|
||||
}
|
||||
this.lastSuccessfulEnsureByteChunk = chunk;
|
||||
|
@ -1387,7 +1395,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (end <= this.initialDataLength) {
|
||||
if (end <= this.progressiveDataLength) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1395,7 +1403,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||
var beginChunk = Math.floor(begin / chunkSize);
|
||||
var endChunk = Math.floor((end - 1) / chunkSize) + 1;
|
||||
for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
|
||||
if (!(chunk in this.loadedChunks)) {
|
||||
if (!this.loadedChunks[chunk]) {
|
||||
throw new MissingDataException(begin, end);
|
||||
}
|
||||
}
|
||||
|
@ -1404,13 +1412,13 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||
nextEmptyChunk: function ChunkedStream_nextEmptyChunk(beginChunk) {
|
||||
var chunk, n;
|
||||
for (chunk = beginChunk, n = this.numChunks; chunk < n; ++chunk) {
|
||||
if (!(chunk in this.loadedChunks)) {
|
||||
if (!this.loadedChunks[chunk]) {
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
// Wrap around to beginning
|
||||
for (chunk = 0; chunk < beginChunk; ++chunk) {
|
||||
if (!(chunk in this.loadedChunks)) {
|
||||
if (!this.loadedChunks[chunk]) {
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
|
@ -1418,7 +1426,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||
},
|
||||
|
||||
hasChunk: function ChunkedStream_hasChunk(chunk) {
|
||||
return chunk in this.loadedChunks;
|
||||
return !!this.loadedChunks[chunk];
|
||||
},
|
||||
|
||||
get length() {
|
||||
|
@ -1517,7 +1525,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||
var endChunk = Math.floor((this.end - 1) / chunkSize) + 1;
|
||||
var missingChunks = [];
|
||||
for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
|
||||
if (!(chunk in this.loadedChunks)) {
|
||||
if (!this.loadedChunks[chunk]) {
|
||||
missingChunks.push(chunk);
|
||||
}
|
||||
}
|
||||
|
@ -1575,28 +1583,16 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
|
|||
this.chunksNeededByRequest = {};
|
||||
this.requestsByChunk = {};
|
||||
this.callbacksByRequest = {};
|
||||
this.progressiveDataLength = 0;
|
||||
|
||||
this._loadedStreamCapability = createPromiseCapability();
|
||||
|
||||
if (args.initialData) {
|
||||
this.setInitialData(args.initialData);
|
||||
this.onReceiveData({chunk: args.initialData});
|
||||
}
|
||||
}
|
||||
|
||||
ChunkedStreamManager.prototype = {
|
||||
|
||||
setInitialData: function ChunkedStreamManager_setInitialData(data) {
|
||||
this.stream.onReceiveInitialData(data);
|
||||
if (this.stream.allChunksLoaded()) {
|
||||
this._loadedStreamCapability.resolve(this.stream);
|
||||
} else if (this.msgHandler) {
|
||||
this.msgHandler.send('DocProgress', {
|
||||
loaded: data.length,
|
||||
total: this.length
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onLoadedStream: function ChunkedStreamManager_getLoadedStream() {
|
||||
return this._loadedStreamCapability.promise;
|
||||
},
|
||||
|
@ -1734,13 +1730,21 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
|
|||
|
||||
onReceiveData: function ChunkedStreamManager_onReceiveData(args) {
|
||||
var chunk = args.chunk;
|
||||
var begin = args.begin;
|
||||
var isProgressive = args.begin === undefined;
|
||||
var begin = isProgressive ? this.progressiveDataLength : args.begin;
|
||||
var end = begin + chunk.byteLength;
|
||||
|
||||
var beginChunk = this.getBeginChunk(begin);
|
||||
var endChunk = this.getEndChunk(end);
|
||||
var beginChunk = Math.floor(begin / this.chunkSize);
|
||||
var endChunk = end < this.length ? Math.floor(end / this.chunkSize) :
|
||||
Math.ceil(end / this.chunkSize);
|
||||
|
||||
if (isProgressive) {
|
||||
this.stream.onReceiveProgressiveData(chunk);
|
||||
this.progressiveDataLength = end;
|
||||
} else {
|
||||
this.stream.onReceiveData(begin, chunk);
|
||||
}
|
||||
|
||||
this.stream.onReceiveData(begin, chunk);
|
||||
if (this.stream.allChunksLoaded()) {
|
||||
this._loadedStreamCapability.resolve(this.stream);
|
||||
}
|
||||
|
@ -1877,6 +1881,10 @@ var BasePdfManager = (function BasePdfManagerClosure() {
|
|||
return new NotImplementedException();
|
||||
},
|
||||
|
||||
sendProgressiveData: function BasePdfManager_sendProgressiveData(chunk) {
|
||||
return new NotImplementedException();
|
||||
},
|
||||
|
||||
updatePassword: function BasePdfManager_updatePassword(password) {
|
||||
this.pdfDocument.xref.password = this.password = password;
|
||||
if (this._passwordChangedCapability) {
|
||||
|
@ -2013,6 +2021,11 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() {
|
|||
this.streamManager.requestAllChunks();
|
||||
};
|
||||
|
||||
NetworkPdfManager.prototype.sendProgressiveData =
|
||||
function NetworkPdfManager_sendProgressiveData(chunk) {
|
||||
this.streamManager.onReceiveData({ chunk: chunk });
|
||||
};
|
||||
|
||||
NetworkPdfManager.prototype.onLoadedStream =
|
||||
function NetworkPdfManager_getLoadedStream() {
|
||||
return this.streamManager.onLoadedStream();
|
||||
|
@ -2959,6 +2972,38 @@ var Catalog = (function CatalogClosure() {
|
|||
}
|
||||
return shadow(this, 'destinations', dests);
|
||||
},
|
||||
getDestination: function Catalog_getDestination(destinationId) {
|
||||
function fetchDestination(dest) {
|
||||
return isDict(dest) ? dest.get('D') : dest;
|
||||
}
|
||||
|
||||
var xref = this.xref;
|
||||
var dest, nameTreeRef, nameDictionaryRef;
|
||||
var obj = this.catDict.get('Names');
|
||||
if (obj && obj.has('Dests')) {
|
||||
nameTreeRef = obj.getRaw('Dests');
|
||||
} else if (this.catDict.has('Dests')) {
|
||||
nameDictionaryRef = this.catDict.get('Dests');
|
||||
}
|
||||
|
||||
if (nameDictionaryRef) {
|
||||
// reading simple destination dictionary
|
||||
obj = nameDictionaryRef;
|
||||
obj.forEach(function catalogForEach(key, value) {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
if (key === destinationId) {
|
||||
dest = fetchDestination(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (nameTreeRef) {
|
||||
var nameTree = new NameTree(nameTreeRef, xref);
|
||||
dest = fetchDestination(nameTree.get(destinationId));
|
||||
}
|
||||
return dest;
|
||||
},
|
||||
get attachments() {
|
||||
var xref = this.xref;
|
||||
var attachments = null, nameTreeRef;
|
||||
|
@ -3860,6 +3905,76 @@ var NameTree = (function NameTreeClosure() {
|
|||
}
|
||||
}
|
||||
return dict;
|
||||
},
|
||||
|
||||
get: function NameTree_get(destinationId) {
|
||||
if (!this.root) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var xref = this.xref;
|
||||
var kidsOrNames = xref.fetchIfRef(this.root);
|
||||
var loopCount = 0;
|
||||
var MAX_NAMES_LEVELS = 10;
|
||||
var l, r, m;
|
||||
|
||||
// Perform a binary search to quickly find the entry that
|
||||
// contains the named destination we are looking for.
|
||||
while (kidsOrNames.has('Kids')) {
|
||||
loopCount++;
|
||||
if (loopCount > MAX_NAMES_LEVELS) {
|
||||
warn('Search depth limit for named destionations has been reached.');
|
||||
return null;
|
||||
}
|
||||
|
||||
var kids = kidsOrNames.get('Kids');
|
||||
if (!isArray(kids)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
l = 0;
|
||||
r = kids.length - 1;
|
||||
while (l <= r) {
|
||||
m = (l + r) >> 1;
|
||||
var kid = xref.fetchIfRef(kids[m]);
|
||||
var limits = kid.get('Limits');
|
||||
|
||||
if (destinationId < limits[0]) {
|
||||
r = m - 1;
|
||||
} else if (destinationId > limits[1]) {
|
||||
l = m + 1;
|
||||
} else {
|
||||
kidsOrNames = xref.fetchIfRef(kids[m]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (l > r) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, then we have found the right entry. Now
|
||||
// go through the named destinations in the Named dictionary
|
||||
// until we find the exact destination we're looking for.
|
||||
var names = kidsOrNames.get('Names');
|
||||
if (isArray(names)) {
|
||||
// Perform a binary search to reduce the lookup time.
|
||||
l = 0;
|
||||
r = names.length - 2;
|
||||
while (l <= r) {
|
||||
// Check only even indices (0, 2, 4, ...) because the
|
||||
// odd indices contain the actual D array.
|
||||
m = (l + r) & ~1;
|
||||
if (destinationId < names[m]) {
|
||||
r = m - 2;
|
||||
} else if (destinationId > names[m]) {
|
||||
l = m + 2;
|
||||
} else {
|
||||
return xref.fetchIfRef(names[m + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
return NameTree;
|
||||
|
@ -15857,6 +15972,12 @@ var Font = (function FontClosure() {
|
|||
for (var code in GlyphMapForStandardFonts) {
|
||||
map[+code] = GlyphMapForStandardFonts[code];
|
||||
}
|
||||
var isIdentityUnicode = this.toUnicode instanceof IdentityToUnicodeMap;
|
||||
if (!isIdentityUnicode) {
|
||||
this.toUnicode.forEach(function(charCode, unicodeCharCode) {
|
||||
map[+charCode] = unicodeCharCode;
|
||||
});
|
||||
}
|
||||
this.toFontChar = map;
|
||||
this.toUnicode = new ToUnicodeMap(map);
|
||||
} else if (/Symbol/i.test(fontName)) {
|
||||
|
@ -15868,6 +15989,13 @@ var Font = (function FontClosure() {
|
|||
}
|
||||
this.toFontChar[charCode] = fontChar;
|
||||
}
|
||||
for (charCode in properties.differences) {
|
||||
fontChar = GlyphsUnicode[properties.differences[charCode]];
|
||||
if (!fontChar) {
|
||||
continue;
|
||||
}
|
||||
this.toFontChar[charCode] = fontChar;
|
||||
}
|
||||
} else if (/Dingbats/i.test(fontName)) {
|
||||
var dingbats = Encodings.ZapfDingbatsEncoding;
|
||||
for (charCode in dingbats) {
|
||||
|
@ -15928,6 +16056,9 @@ var Font = (function FontClosure() {
|
|||
|
||||
var data;
|
||||
switch (type) {
|
||||
case 'MMType1':
|
||||
info('MMType1 font (' + name + '), falling back to Type1.');
|
||||
/* falls through */
|
||||
case 'Type1':
|
||||
case 'CIDFontType0':
|
||||
this.mimetype = 'font/opentype';
|
||||
|
@ -31206,7 +31337,7 @@ var JpegStream = (function JpegStreamClosure() {
|
|||
var jpegImage = new JpegImage();
|
||||
|
||||
// checking if values needs to be transformed before conversion
|
||||
if (this.dict && isArray(this.dict.get('Decode'))) {
|
||||
if (this.forceRGB && this.dict && isArray(this.dict.get('Decode'))) {
|
||||
var decodeArr = this.dict.get('Decode');
|
||||
var bitsPerComponent = this.dict.get('BitsPerComponent') || 8;
|
||||
var decodeArrLength = decodeArr.length;
|
||||
|
@ -32840,6 +32971,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
|
|||
httpHeaders: source.httpHeaders,
|
||||
withCredentials: source.withCredentials
|
||||
});
|
||||
var cachedChunks = [];
|
||||
var fullRequestXhrId = networkManager.requestFull({
|
||||
onHeadersReceived: function onHeadersReceived() {
|
||||
if (disableRange) {
|
||||
|
@ -32870,11 +33002,18 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
|
|||
return;
|
||||
}
|
||||
|
||||
// NOTE: by cancelling the full request, and then issuing range
|
||||
// requests, there will be an issue for sites where you can only
|
||||
// request the pdf once. However, if this is the case, then the
|
||||
// server should not be returning that it can support range requests.
|
||||
networkManager.abortRequest(fullRequestXhrId);
|
||||
if (networkManager.isStreamingRequest(fullRequestXhrId)) {
|
||||
// We can continue fetching when progressive loading is enabled,
|
||||
// and we don't need the autoFetch feature.
|
||||
source.disableAutoFetch = true;
|
||||
} else {
|
||||
// NOTE: by cancelling the full request, and then issuing range
|
||||
// requests, there will be an issue for sites where you can only
|
||||
// request the pdf once. However, if this is the case, then the
|
||||
// server should not be returning that it can support range
|
||||
// requests.
|
||||
networkManager.abortRequest(fullRequestXhrId);
|
||||
}
|
||||
|
||||
try {
|
||||
pdfManager = new NetworkPdfManager(source, handler);
|
||||
|
@ -32884,10 +33023,44 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
|
|||
}
|
||||
},
|
||||
|
||||
onProgressiveData: source.disableStream ? null :
|
||||
function onProgressiveData(chunk) {
|
||||
if (!pdfManager) {
|
||||
cachedChunks.push(chunk);
|
||||
return;
|
||||
}
|
||||
pdfManager.sendProgressiveData(chunk);
|
||||
},
|
||||
|
||||
onDone: function onDone(args) {
|
||||
if (pdfManager) {
|
||||
return; // already processed
|
||||
}
|
||||
|
||||
var pdfFile;
|
||||
if (args === null) {
|
||||
// TODO add some streaming manager, e.g. for unknown length files.
|
||||
// The data was returned in the onProgressiveData, combining...
|
||||
var pdfFileLength = 0, pos = 0;
|
||||
cachedChunks.forEach(function (chunk) {
|
||||
pdfFileLength += chunk.byteLength;
|
||||
});
|
||||
if (source.length && pdfFileLength !== source.length) {
|
||||
warn('reported HTTP length is different from actual');
|
||||
}
|
||||
var pdfFileArray = new Uint8Array(pdfFileLength);
|
||||
cachedChunks.forEach(function (chunk) {
|
||||
pdfFileArray.set(new Uint8Array(chunk), pos);
|
||||
pos += chunk.byteLength;
|
||||
});
|
||||
pdfFile = pdfFileArray.buffer;
|
||||
} else {
|
||||
pdfFile = args.chunk;
|
||||
}
|
||||
|
||||
// the data is array, instantiating directly from it
|
||||
try {
|
||||
pdfManager = new LocalPdfManager(args.chunk, source.password);
|
||||
pdfManager = new LocalPdfManager(pdfFile, source.password);
|
||||
pdfManagerCapability.resolve();
|
||||
} catch (ex) {
|
||||
pdfManagerCapability.reject(ex);
|
||||
|
@ -32982,6 +33155,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
|
|||
PDFJS.cMapPacked = data.cMapPacked === true;
|
||||
|
||||
getPdfManager(data).then(function () {
|
||||
handler.send('PDFManagerReady', null);
|
||||
pdfManager.onLoadedStream().then(function(stream) {
|
||||
handler.send('DataLoaded', { length: stream.bytes.byteLength });
|
||||
});
|
||||
|
@ -33036,6 +33210,12 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
|
|||
}
|
||||
);
|
||||
|
||||
handler.on('GetDestination',
|
||||
function wphSetupGetDestination(data) {
|
||||
return pdfManager.ensureCatalog('getDestination', [ data.id ]);
|
||||
}
|
||||
);
|
||||
|
||||
handler.on('GetAttachments',
|
||||
function wphSetupGetAttachments(data) {
|
||||
return pdfManager.ensureCatalog('attachments');
|
||||
|
|
|
@ -62,11 +62,11 @@ var NetworkManager = (function NetworkManagerClosure() {
|
|||
return data;
|
||||
}
|
||||
var length = data.length;
|
||||
var buffer = new Uint8Array(length);
|
||||
var array = new Uint8Array(length);
|
||||
for (var i = 0; i < length; i++) {
|
||||
buffer[i] = data.charCodeAt(i) & 0xFF;
|
||||
array[i] = data.charCodeAt(i) & 0xFF;
|
||||
}
|
||||
return buffer;
|
||||
return array.buffer;
|
||||
}
|
||||
|
||||
NetworkManager.prototype = {
|
||||
|
@ -81,11 +81,11 @@ var NetworkManager = (function NetworkManagerClosure() {
|
|||
return this.request(args);
|
||||
},
|
||||
|
||||
requestFull: function NetworkManager_requestRange(listeners) {
|
||||
requestFull: function NetworkManager_requestFull(listeners) {
|
||||
return this.request(listeners);
|
||||
},
|
||||
|
||||
request: function NetworkManager_requestRange(args) {
|
||||
request: function NetworkManager_request(args) {
|
||||
var xhr = this.getXhr();
|
||||
var xhrId = this.currXhrId++;
|
||||
var pendingRequest = this.pendingRequests[xhrId] = {
|
||||
|
@ -109,27 +109,54 @@ var NetworkManager = (function NetworkManagerClosure() {
|
|||
pendingRequest.expectedStatus = 200;
|
||||
}
|
||||
|
||||
xhr.responseType = 'arraybuffer';
|
||||
|
||||
if (args.onProgress) {
|
||||
xhr.onprogress = args.onProgress;
|
||||
if (args.onProgressiveData) {
|
||||
xhr.responseType = 'moz-chunked-arraybuffer';
|
||||
if (xhr.responseType === 'moz-chunked-arraybuffer') {
|
||||
pendingRequest.onProgressiveData = args.onProgressiveData;
|
||||
pendingRequest.mozChunked = true;
|
||||
} else {
|
||||
xhr.responseType = 'arraybuffer';
|
||||
}
|
||||
} else {
|
||||
xhr.responseType = 'arraybuffer';
|
||||
}
|
||||
|
||||
if (args.onError) {
|
||||
xhr.onerror = function(evt) {
|
||||
args.onError(xhr.status);
|
||||
};
|
||||
}
|
||||
xhr.onreadystatechange = this.onStateChange.bind(this, xhrId);
|
||||
xhr.onprogress = this.onProgress.bind(this, xhrId);
|
||||
|
||||
pendingRequest.onHeadersReceived = args.onHeadersReceived;
|
||||
pendingRequest.onDone = args.onDone;
|
||||
pendingRequest.onError = args.onError;
|
||||
pendingRequest.onProgress = args.onProgress;
|
||||
|
||||
xhr.send(null);
|
||||
|
||||
return xhrId;
|
||||
},
|
||||
|
||||
onProgress: function NetworkManager_onProgress(xhrId, evt) {
|
||||
var pendingRequest = this.pendingRequests[xhrId];
|
||||
if (!pendingRequest) {
|
||||
// Maybe abortRequest was called...
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingRequest.mozChunked) {
|
||||
var chunk = getArrayBuffer(pendingRequest.xhr);
|
||||
pendingRequest.onProgressiveData(chunk);
|
||||
}
|
||||
|
||||
var onProgress = pendingRequest.onProgress;
|
||||
if (onProgress) {
|
||||
onProgress(evt);
|
||||
}
|
||||
},
|
||||
|
||||
onStateChange: function NetworkManager_onStateChange(xhrId, evt) {
|
||||
var pendingRequest = this.pendingRequests[xhrId];
|
||||
if (!pendingRequest) {
|
||||
|
@ -190,6 +217,8 @@ var NetworkManager = (function NetworkManagerClosure() {
|
|||
begin: begin,
|
||||
chunk: chunk
|
||||
});
|
||||
} else if (pendingRequest.onProgressiveData) {
|
||||
pendingRequest.onDone(null);
|
||||
} else {
|
||||
pendingRequest.onDone({
|
||||
begin: 0,
|
||||
|
@ -209,6 +238,10 @@ var NetworkManager = (function NetworkManagerClosure() {
|
|||
return this.pendingRequests[xhrId].xhr;
|
||||
},
|
||||
|
||||
isStreamingRequest: function NetworkManager_isStreamingRequest(xhrId) {
|
||||
return !!(this.pendingRequests[xhrId].onProgressiveData);
|
||||
},
|
||||
|
||||
isPendingRequest: function NetworkManager_isPendingRequest(xhrId) {
|
||||
return xhrId in this.pendingRequests;
|
||||
},
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
/* jshint esnext:true */
|
||||
/* globals Components, PdfjsContentUtils, PdfJs */
|
||||
/* globals Components, PdfjsContentUtils, PdfJs, Services */
|
||||
|
||||
'use strict';
|
||||
|
||||
|
|
|
@ -112,13 +112,20 @@ var FontInspector = (function FontInspectorClosure() {
|
|||
return moreInfo;
|
||||
}
|
||||
var moreInfo = properties(fontObj, ['name', 'type']);
|
||||
var m = /url\(['"]?([^\)"']+)/.exec(url);
|
||||
var fontName = fontObj.loadedName;
|
||||
var font = document.createElement('div');
|
||||
var name = document.createElement('span');
|
||||
name.textContent = fontName;
|
||||
var download = document.createElement('a');
|
||||
download.href = m[1];
|
||||
if (url) {
|
||||
url = /url\(['"]?([^\)"']+)/.exec(url);
|
||||
download.href = url[1];
|
||||
} else if (fontObj.data) {
|
||||
url = URL.createObjectURL(new Blob([fontObj.data], {
|
||||
type: fontObj.mimeType
|
||||
}));
|
||||
}
|
||||
download.href = url;
|
||||
download.textContent = 'Download';
|
||||
var logIt = document.createElement('a');
|
||||
logIt.href = '';
|
||||
|
@ -211,6 +218,7 @@ var StepperManager = (function StepperManagerClosure() {
|
|||
},
|
||||
selectStepper: function selectStepper(pageIndex, selectPanel) {
|
||||
var i;
|
||||
pageIndex = pageIndex | 0;
|
||||
if (selectPanel) {
|
||||
this.manager.selectPanel(this);
|
||||
}
|
||||
|
@ -419,7 +427,7 @@ var Stepper = (function StepperClosure() {
|
|||
var allRows = this.panel.getElementsByClassName('line');
|
||||
for (var x = 0, xx = allRows.length; x < xx; ++x) {
|
||||
var row = allRows[x];
|
||||
if (parseInt(row.dataset.idx, 10) === idx) {
|
||||
if ((row.dataset.idx | 0) === idx) {
|
||||
row.style.backgroundColor = 'rgb(251,250,207)';
|
||||
row.scrollIntoView();
|
||||
} else {
|
||||
|
|
Двоичные данные
browser/extensions/pdfjs/content/web/images/loading-small.png
Двоичные данные
browser/extensions/pdfjs/content/web/images/loading-small.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 3.5 KiB После Ширина: | Высота: | Размер: 7.2 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 16 KiB |
|
@ -1,4 +1,4 @@
|
|||
/* Copyright 2012 Mozilla Foundation
|
||||
/* Copyright 2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -13,6 +13,141 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.textLayer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.textLayer > div {
|
||||
color: transparent;
|
||||
position: absolute;
|
||||
white-space: pre;
|
||||
cursor: text;
|
||||
-moz-transform-origin: 0% 0%;
|
||||
transform-origin: 0% 0%;
|
||||
}
|
||||
|
||||
.textLayer .highlight {
|
||||
margin: -1px;
|
||||
padding: 1px;
|
||||
|
||||
background-color: rgb(180, 0, 170);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.textLayer .highlight.begin {
|
||||
border-radius: 4px 0px 0px 4px;
|
||||
}
|
||||
|
||||
.textLayer .highlight.end {
|
||||
border-radius: 0px 4px 4px 0px;
|
||||
}
|
||||
|
||||
.textLayer .highlight.middle {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.textLayer .highlight.selected {
|
||||
background-color: rgb(0, 100, 0);
|
||||
}
|
||||
|
||||
.pdfViewer .canvasWrapper {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pdfViewer .page {
|
||||
direction: ltr;
|
||||
width: 816px;
|
||||
height: 1056px;
|
||||
margin: 1px auto -8px auto;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
border: 9px solid transparent;
|
||||
background-clip: content-box;
|
||||
border-image: url(images/shadow.png) 9 9 repeat;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.pdfViewer .page canvas {
|
||||
margin: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pdfViewer .page .loadingIcon {
|
||||
position: absolute;
|
||||
display: block;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: url('images/loading-icon.gif') center no-repeat;
|
||||
}
|
||||
|
||||
.pdfViewer .page .annotLink > a:hover {
|
||||
opacity: 0.2;
|
||||
background: #ff0;
|
||||
box-shadow: 0px 2px 10px #ff0;
|
||||
}
|
||||
|
||||
:-moz-full-screen .pdfViewer .page {
|
||||
margin-bottom: 100%;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
:fullscreen .pdfViewer .page {
|
||||
margin-bottom: 100%;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.pdfViewer .page .annotationHighlight {
|
||||
position: absolute;
|
||||
border: 2px #FFFF99 solid;
|
||||
}
|
||||
|
||||
.pdfViewer .page .annotText > img {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pdfViewer .page .annotTextContentWrapper {
|
||||
position: absolute;
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
.pdfViewer .page .annotTextContent {
|
||||
z-index: 200;
|
||||
float: left;
|
||||
max-width: 20em;
|
||||
background-color: #FFFF99;
|
||||
box-shadow: 0px 2px 5px #333;
|
||||
border-radius: 2px;
|
||||
padding: 0.6em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pdfViewer .page .annotTextContent > h1 {
|
||||
font-size: 1em;
|
||||
border-bottom: 1px solid #000000;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
||||
.pdfViewer .page .annotTextContent > p {
|
||||
padding-top: 0.2em;
|
||||
}
|
||||
|
||||
.pdfViewer .page .annotLink > a {
|
||||
position: absolute;
|
||||
font-size: 1em;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
@ -65,16 +200,6 @@ select {
|
|||
cursor: none;
|
||||
}
|
||||
|
||||
:-moz-full-screen .page {
|
||||
margin-bottom: 100%;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
:fullscreen .page {
|
||||
margin-bottom: 100%;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
:-moz-full-screen a:not(.internalLink) {
|
||||
display: none;
|
||||
}
|
||||
|
@ -984,6 +1109,12 @@ html[dir='rtl'] .verticalToolbarSeparator {
|
|||
width: 40px;
|
||||
}
|
||||
|
||||
.toolbarField.pageNumber.visiblePageIsLoading {
|
||||
background-image: url(images/loading-small.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 1px;
|
||||
}
|
||||
|
||||
.toolbarField:hover {
|
||||
background-color: hsla(0,0%,100%,.11);
|
||||
border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.43) hsla(0,0%,0%,.45);
|
||||
|
@ -1167,135 +1298,17 @@ html[dir='rtl'] .attachmentsItem > button {
|
|||
cursor: default;
|
||||
}
|
||||
|
||||
.canvasWrapper {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
canvas {
|
||||
margin: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.page {
|
||||
direction: ltr;
|
||||
width: 816px;
|
||||
height: 1056px;
|
||||
margin: 1px auto -8px auto;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
border: 9px solid transparent;
|
||||
background-clip: content-box;
|
||||
border-image: url(images/shadow.png) 9 9 repeat;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.annotLink > a:hover {
|
||||
opacity: 0.2;
|
||||
background: #ff0;
|
||||
box-shadow: 0px 2px 10px #ff0;
|
||||
}
|
||||
|
||||
.loadingIcon {
|
||||
position: absolute;
|
||||
display: block;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: url('images/loading-icon.gif') center no-repeat;
|
||||
}
|
||||
|
||||
.textLayer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.textLayer > div {
|
||||
color: transparent;
|
||||
position: absolute;
|
||||
white-space: pre;
|
||||
cursor: text;
|
||||
-moz-transform-origin: 0% 0%;
|
||||
transform-origin: 0% 0%;
|
||||
}
|
||||
|
||||
.textLayer .highlight {
|
||||
margin: -1px;
|
||||
padding: 1px;
|
||||
|
||||
background-color: rgba(180, 0, 170, 0.2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.textLayer .highlight.begin {
|
||||
border-radius: 4px 0px 0px 4px;
|
||||
}
|
||||
|
||||
.textLayer .highlight.end {
|
||||
border-radius: 0px 4px 4px 0px;
|
||||
}
|
||||
|
||||
.textLayer .highlight.middle {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.textLayer .highlight.selected {
|
||||
background-color: rgba(0, 100, 0, 0.2);
|
||||
}
|
||||
|
||||
/* TODO: file FF bug to support ::-moz-selection:window-inactive
|
||||
so we can override the opaque grey background when the window is inactive;
|
||||
see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */
|
||||
::selection { background:rgba(0,0,255,0.3); }
|
||||
::-moz-selection { background:rgba(0,0,255,0.3); }
|
||||
::selection { background: rgba(0,0,255,0.3); }
|
||||
::-moz-selection { background: rgba(0,0,255,0.3); }
|
||||
|
||||
.annotationHighlight {
|
||||
position: absolute;
|
||||
border: 2px #FFFF99 solid;
|
||||
}
|
||||
|
||||
.annotText > img {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.annotTextContentWrapper {
|
||||
position: absolute;
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
.annotTextContent {
|
||||
z-index: 200;
|
||||
float: left;
|
||||
max-width: 20em;
|
||||
background-color: #FFFF99;
|
||||
box-shadow: 0px 2px 5px #333;
|
||||
border-radius: 2px;
|
||||
padding: 0.6em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.annotTextContent > h1 {
|
||||
font-size: 1em;
|
||||
border-bottom: 1px solid #000000;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
||||
.annotTextContent > p {
|
||||
padding-top: 0.2em;
|
||||
}
|
||||
|
||||
.annotLink > a {
|
||||
position: absolute;
|
||||
font-size: 1em;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.textLayer ::selection { background: rgb(0,0,255); }
|
||||
.textLayer ::-moz-selection { background: rgb(0,0,255); }
|
||||
.textLayer {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
#errorWrapper {
|
||||
|
@ -1477,11 +1490,9 @@ html[dir='rtl'] #documentPropertiesOverlay .row > * {
|
|||
.debuggerShowText {
|
||||
background: none repeat scroll 0 0 yellow;
|
||||
color: blue;
|
||||
opacity: 0.3;
|
||||
}
|
||||
.debuggerHideText:hover {
|
||||
background: none repeat scroll 0 0 yellow;
|
||||
opacity: 0.3;
|
||||
}
|
||||
#PDFBug .stats {
|
||||
font-family: courier;
|
||||
|
@ -1561,6 +1572,12 @@ html[dir='rtl'] #documentPropertiesOverlay .row > * {
|
|||
left: 186px;
|
||||
}
|
||||
|
||||
.toolbarField.pageNumber.visiblePageIsLoading,
|
||||
#findInput[data-status="pending"] {
|
||||
background-image: url(images/loading-small@2x.png);
|
||||
background-size: 16px 17px;
|
||||
}
|
||||
|
||||
.dropdownToolbarButton {
|
||||
background: url(images/toolbarButton-menuArrows@2x.png) no-repeat;
|
||||
background-size: 7px 16px;
|
||||
|
@ -1710,6 +1727,8 @@ html[dir='rtl'] #documentPropertiesOverlay .row > * {
|
|||
display: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background-clip: content-box;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.page[data-loaded] {
|
||||
|
@ -1731,6 +1750,7 @@ html[dir='rtl'] #documentPropertiesOverlay .row > * {
|
|||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -245,7 +245,7 @@ http://sourceforge.net/adobe/cmap/wiki/License/
|
|||
</menu>
|
||||
|
||||
<div id="viewerContainer" tabindex="0">
|
||||
<div id="viewer"></div>
|
||||
<div id="viewer" class="pdfViewer"></div>
|
||||
</div>
|
||||
|
||||
<div id="errorWrapper" hidden='true'>
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -97,7 +97,7 @@ quit-button.tooltiptext.linux2 = Quit %1$S (%2$S)
|
|||
# %2$S is the keyboard shortcut
|
||||
quit-button.tooltiptext.mac = Quit %1$S (%2$S)
|
||||
|
||||
# LOCALIZATION NOTE(loop-call-button2.label2): This is a brand name, request
|
||||
# LOCALIZATION NOTE(loop-call-button3.label): This is a brand name, request
|
||||
# approval before you change it.
|
||||
loop-call-button3.label = Hello
|
||||
loop-call-button2.tooltiptext = Start a conversation
|
||||
|
|
|
@ -2068,15 +2068,6 @@ toolbarbutton.chevron > .toolbarbutton-icon {
|
|||
|
||||
/* Social toolbar item */
|
||||
|
||||
#social-provider-button {
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
list-style-image: url(chrome://browser/skin/social/services-16.png);
|
||||
}
|
||||
|
||||
#social-provider-button > .toolbarbutton-menu-dropmarker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="servicesInstall"] {
|
||||
list-style-image: url(chrome://browser/skin/social/services-64.png);
|
||||
}
|
||||
|
|
|
@ -4366,64 +4366,6 @@ menulist.translate-infobar-element > .menulist-dropmarker {
|
|||
border-radius: 1px;
|
||||
}
|
||||
|
||||
/* === social toolbar button === */
|
||||
|
||||
#social-toolbar-item > .toolbarbutton-1 {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
#social-toolbar-item > .toolbarbutton-1:-moz-locale-dir(ltr) {
|
||||
-moz-border-end-width: 0;
|
||||
}
|
||||
#social-toolbar-item > .toolbarbutton-1:last-child:-moz-locale-dir(ltr) {
|
||||
-moz-border-end-width: 1px;
|
||||
}
|
||||
#social-toolbar-item > .toolbarbutton-1:-moz-locale-dir(rtl) {
|
||||
-moz-border-start-width: 0;
|
||||
}
|
||||
#social-toolbar-item > .toolbarbutton-1:first-child:-moz-locale-dir(rtl) {
|
||||
-moz-border-start-width: 1px;
|
||||
}
|
||||
|
||||
#social-toolbar-item > .toolbarbutton-1:-moz-locale-dir(ltr):first-child,
|
||||
#social-toolbar-item > .toolbarbutton-1:-moz-locale-dir(rtl):last-child {
|
||||
margin-left: 4px;
|
||||
border-top-left-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
#social-toolbar-item > .toolbarbutton-1:-moz-locale-dir(rtl):first-child,
|
||||
#social-toolbar-item > .toolbarbutton-1:-moz-locale-dir(ltr):last-child {
|
||||
margin-right: 4px;
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
|
||||
#social-toolbar-item > toolbaritem {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#social-provider-button {
|
||||
list-style-image: url(chrome://browser/skin/social/services-16.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
#social-provider-button {
|
||||
list-style-image: url(chrome://browser/skin/social/services-16@2x.png);
|
||||
}
|
||||
#social-provider-button > .toolbarbutton-icon {
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
#social-provider-button > .toolbarbutton-menu-dropmarker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="servicesInstall"] {
|
||||
list-style-image: url(chrome://browser/skin/social/services-64.png);
|
||||
}
|
||||
|
|
|
@ -623,7 +623,7 @@ toolbar[brighttext] .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
|
|||
-moz-padding-end: 5px;
|
||||
}
|
||||
|
||||
#nav-bar .toolbarbutton-1[type=menu]:not(#back-button):not(#forward-button):not(#feed-button):not(#social-provider-button):not(#PanelUI-menu-button) {
|
||||
#nav-bar .toolbarbutton-1[type=menu]:not(#back-button):not(#forward-button):not(#feed-button):not(#PanelUI-menu-button) {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
@ -698,7 +698,7 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
|
|||
width: 32px;
|
||||
}
|
||||
|
||||
#nav-bar .toolbarbutton-1[type=menu]:not(#back-button):not(#forward-button):not(#feed-button):not(#social-provider-button):not(#PanelUI-menu-button) > .toolbarbutton-icon,
|
||||
#nav-bar .toolbarbutton-1[type=menu]:not(#back-button):not(#forward-button):not(#feed-button):not(#PanelUI-menu-button) > .toolbarbutton-icon,
|
||||
#nav-bar .toolbarbutton-1[type=menu] > .toolbarbutton-text /* hack for add-ons that forcefully display the label */ {
|
||||
-moz-padding-end: 17px;
|
||||
}
|
||||
|
@ -2677,15 +2677,6 @@ notification[value="translation"] {
|
|||
|
||||
/* Social toolbar item */
|
||||
|
||||
#social-provider-button {
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
list-style-image: url(chrome://browser/skin/social/services-16.png);
|
||||
}
|
||||
|
||||
#social-provider-button > .toolbarbutton-menu-dropmarker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#switch-to-metro-button[cui-areatype="toolbar"] {
|
||||
list-style-image: url(chrome://browser/skin/Metro_Glyph.png);
|
||||
}
|
||||
|
|
|
@ -1788,7 +1788,7 @@ public class BrowserApp extends GeckoApp
|
|||
mTabsPanel.prepareTabsAnimation(mMainLayoutAnimator);
|
||||
mBrowserToolbar.triggerTabsPanelTransition(mMainLayoutAnimator, areTabsShown());
|
||||
|
||||
// If the tabs layout is animating onto the screen, pin the dynamic
|
||||
// If the tabs panel is animating onto the screen, pin the dynamic
|
||||
// toolbar.
|
||||
if (mDynamicToolbar.isEnabled()) {
|
||||
if (width > 0 && height > 0) {
|
||||
|
@ -2376,7 +2376,7 @@ public class BrowserApp extends GeckoApp
|
|||
}
|
||||
|
||||
/**
|
||||
* Hides certain UI elements (e.g. button toast, tabs tray) when the
|
||||
* Hides certain UI elements (e.g. button toast, tabs panel) when the
|
||||
* user touches the main layout.
|
||||
*/
|
||||
private class HideOnTouchListener implements TouchEventInterceptor {
|
||||
|
@ -2412,7 +2412,7 @@ public class BrowserApp extends GeckoApp
|
|||
return false;
|
||||
}
|
||||
|
||||
// If the tab tray is showing, hide the tab tray and don't send the event to content.
|
||||
// If the tabs panel is showing, hide the tab panel and don't send the event to content.
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && autoHideTabs()) {
|
||||
mIsHidingTabs = true;
|
||||
return true;
|
||||
|
|
|
@ -128,7 +128,7 @@ public class RemoteTabsExpandableListAdapter extends BaseExpandableListAdapter {
|
|||
lastModifiedView.setText(TabsAccessor.getLastSyncedString(context, now, client.lastModified));
|
||||
|
||||
// These views exists only in some of our group views: they are present
|
||||
// for the home panel groups and not for the tabs tray groups.
|
||||
// for the home panel groups and not for the tabs panel groups.
|
||||
// Therefore, we must handle null.
|
||||
final ImageView deviceTypeView = (ImageView) view.findViewById(R.id.device_type);
|
||||
if (deviceTypeView != null) {
|
||||
|
@ -174,7 +174,7 @@ public class RemoteTabsExpandableListAdapter extends BaseExpandableListAdapter {
|
|||
final RemoteTab tab = client.tabs.get(childPosition);
|
||||
|
||||
// The view is a TwoLinePageRow only for some of our child views: it's
|
||||
// present for the home panel children and not for the tabs tray
|
||||
// present for the home panel children and not for the tabs panel
|
||||
// children. Therefore, we must handle one case manually.
|
||||
if (view instanceof TwoLinePageRow) {
|
||||
((TwoLinePageRow) view).update(tab.title, tab.url);
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
</RelativeLayout>
|
||||
|
||||
<view class="org.mozilla.gecko.tabs.TabsPanel$PanelViewContainer"
|
||||
<view class="org.mozilla.gecko.tabs.TabsPanel$TabsLayoutContainer"
|
||||
android:id="@+id/tabs_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dip"
|
||||
|
@ -41,7 +41,7 @@
|
|||
|
||||
<view class="org.mozilla.gecko.tabs.TabsPanel$TabsLayout"
|
||||
android:id="@+id/normal_tabs"
|
||||
style="@style/TabsList"
|
||||
style="@style/TabsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:choiceMode="singleChoice"
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:paddingTop="4dip"
|
||||
style="@style/TabRowTextAppearance"
|
||||
style="@style/TabLayoutItemTextAppearance"
|
||||
android:textSize="12sp"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:singleLine="true"
|
||||
|
|
|
@ -45,11 +45,11 @@
|
|||
|
||||
</ScrollView>
|
||||
|
||||
<!-- Note: for an unknown reason, scrolling in the TabsListLayout
|
||||
<!-- Note: for an unknown reason, scrolling in the TabsLayout
|
||||
does not work unless it is laid out after the empty view. -->
|
||||
<view class="org.mozilla.gecko.tabs.TabsPanel$TabsLayout"
|
||||
android:id="@+id/private_tabs_tray"
|
||||
style="@style/TabsList"
|
||||
android:id="@+id/private_tabs_layout"
|
||||
style="@style/TabsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:choiceMode="singleChoice"
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:padding="4dip"
|
||||
style="@style/TabRowTextAppearance"
|
||||
style="@style/TabLayoutItemTextAppearance"
|
||||
android:textSize="12sp"
|
||||
android:textColor="#FF222222"
|
||||
android:singleLine="true"
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
android:paddingTop="4dip"
|
||||
android:paddingLeft="8dip"
|
||||
android:paddingRight="4dip"
|
||||
style="@style/TabRowTextAppearance"
|
||||
style="@style/TabLayoutItemTextAppearance"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="14sp"
|
||||
android:singleLine="false"
|
||||
|
|
|
@ -50,14 +50,14 @@
|
|||
|
||||
</RelativeLayout>
|
||||
|
||||
<view class="org.mozilla.gecko.tabs.TabsPanel$PanelViewContainer"
|
||||
<view class="org.mozilla.gecko.tabs.TabsPanel$TabsLayoutContainer"
|
||||
android:id="@+id/tabs_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<view class="org.mozilla.gecko.tabs.TabsPanel$TabsLayout"
|
||||
android:id="@+id/normal_tabs"
|
||||
style="@style/TabsList"
|
||||
style="@style/TabsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:choiceMode="singleChoice"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<resources>
|
||||
|
||||
<style name="TabsList" parent="TabsListBase">
|
||||
<style name="TabsLayout" parent="TabsLayoutBase">
|
||||
<item name="android:orientation">horizontal</item>
|
||||
<item name="android:scrollbars">horizontal</item>
|
||||
</style>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<resources>
|
||||
|
||||
<style name="TabsList" parent="TabsListBase">
|
||||
<style name="TabsLayout" parent="TabsLayoutBase">
|
||||
<item name="android:orientation">vertical</item>
|
||||
<item name="android:scrollbars">vertical</item>
|
||||
</style>
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
<item name="android:orientation">horizontal</item>
|
||||
</style>
|
||||
|
||||
<style name="TabsList" parent="TabsListBase">
|
||||
<style name="TabsLayout" parent="TabsLayoutBase">
|
||||
<item name="android:orientation">horizontal</item>
|
||||
<item name="android:scrollbars">horizontal</item>
|
||||
</style>
|
||||
|
|
|
@ -51,8 +51,8 @@
|
|||
<item name="android:displayOptions">showHome|homeAsUp|showTitle</item>
|
||||
</style>
|
||||
|
||||
<!-- TabsTray ActionBar -->
|
||||
<style name="ActionBar.TabsTray">
|
||||
<!-- TabsLayout ActionBar -->
|
||||
<style name="ActionBar.TabsLayout">
|
||||
<item name="android:visibility">gone</item>
|
||||
</style>
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
<attr name="entryKeys" format="string"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="TabsTray">
|
||||
<declare-styleable name="TabsLayout">
|
||||
<attr name="tabs">
|
||||
<flag name="tabs_normal" value="0x00" />
|
||||
<flag name="tabs_private" value ="0x01" />
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
<dimen name="tabs_strip_button_width">100dp</dimen>
|
||||
<dimen name="tabs_strip_button_padding">18dp</dimen>
|
||||
<dimen name="tabs_strip_shadow_size">1dp</dimen>
|
||||
<dimen name="tabs_tray_horizontal_height">156dp</dimen>
|
||||
<dimen name="tabs_layout_horizontal_height">156dp</dimen>
|
||||
<dimen name="text_selection_handle_width">47dp</dimen>
|
||||
<dimen name="text_selection_handle_height">58dp</dimen>
|
||||
<dimen name="text_selection_handle_shadow">11dp</dimen>
|
||||
|
|
|
@ -480,13 +480,13 @@
|
|||
<item name="android:layout_weight">0.0</item>
|
||||
</style>
|
||||
|
||||
<!-- TabsTray List -->
|
||||
<style name="TabsListBase">
|
||||
<!-- TabsLayout -->
|
||||
<style name="TabsLayoutBase">
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
<item name="android:listSelector">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="TabsList" parent="TabsListBase">
|
||||
<style name="TabsLayout" parent="TabsLayoutBase">
|
||||
<item name="android:orientation">vertical</item>
|
||||
<item name="android:scrollbars">vertical</item>
|
||||
</style>
|
||||
|
@ -615,15 +615,15 @@
|
|||
<item name="android:drawSelectorOnTop">true</item>
|
||||
</style>
|
||||
|
||||
<!-- TabsTray Row -->
|
||||
<style name="TabRowTextAppearance">
|
||||
<!-- TabsLayout Row -->
|
||||
<style name="TabLayoutItemTextAppearance">
|
||||
<item name="android:textColor">#FFFFFFFF</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:ellipsize">middle</item>
|
||||
</style>
|
||||
|
||||
<!-- TabsTray RemoteTabs Row Url -->
|
||||
<style name="TabRowTextAppearance.Url">
|
||||
<!-- TabsLayout RemoteTabs Row Url -->
|
||||
<style name="TabLayoutItemTextAppearance.Url">
|
||||
<item name="android:textColor">#FFA4A7A9</item>
|
||||
</style>
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ class PrivateTabsPanel extends FrameLayout implements CloseAllPanelView {
|
|||
super(context, attrs);
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.private_tabs_panel, this);
|
||||
tabsLayout = (TabsLayout) findViewById(R.id.private_tabs_tray);
|
||||
tabsLayout = (TabsLayout) findViewById(R.id.private_tabs_layout);
|
||||
|
||||
final View emptyView = findViewById(R.id.private_tabs_empty);
|
||||
tabsLayout.setEmptyView(emptyView);
|
||||
|
|
|
@ -47,8 +47,8 @@ class TabsGridLayout extends GridView
|
|||
super(context, attrs, R.attr.tabGridLayoutViewStyle);
|
||||
mContext = context;
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsTray);
|
||||
mIsPrivate = (a.getInt(R.styleable.TabsTray_tabs, 0x0) == 1);
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsLayout);
|
||||
mIsPrivate = (a.getInt(R.styleable.TabsLayout_tabs, 0x0) == 1);
|
||||
a.recycle();
|
||||
|
||||
mTabsAdapter = new TabsGridLayoutAdapter(mContext);
|
||||
|
|
|
@ -65,8 +65,8 @@ class TabsListLayout extends TwoWayView
|
|||
|
||||
setItemsCanFocus(true);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsTray);
|
||||
mIsPrivate = (a.getInt(R.styleable.TabsTray_tabs, 0x0) == 1);
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsLayout);
|
||||
mIsPrivate = (a.getInt(R.styleable.TabsLayout_tabs, 0x0) == 1);
|
||||
a.recycle();
|
||||
|
||||
mTabsAdapter = new TabsListLayoutAdapter(mContext);
|
||||
|
|
|
@ -83,7 +83,7 @@ public class TabsPanel extends LinearLayout
|
|||
private final GeckoApp mActivity;
|
||||
private final LightweightTheme mTheme;
|
||||
private RelativeLayout mHeader;
|
||||
private PanelViewContainer mPanelsContainer;
|
||||
private TabsLayoutContainer mTabsContainer;
|
||||
private PanelView mPanel;
|
||||
private PanelView mPanelNormal;
|
||||
private PanelView mPanelPrivate;
|
||||
|
@ -152,7 +152,7 @@ public class TabsPanel extends LinearLayout
|
|||
|
||||
private void initialize() {
|
||||
mHeader = (RelativeLayout) findViewById(R.id.tabs_panel_header);
|
||||
mPanelsContainer = (PanelViewContainer) findViewById(R.id.tabs_container);
|
||||
mTabsContainer = (TabsLayoutContainer) findViewById(R.id.tabs_container);
|
||||
|
||||
mPanelNormal = (PanelView) findViewById(R.id.normal_tabs);
|
||||
mPanelNormal.setTabsPanel(this);
|
||||
|
@ -279,19 +279,19 @@ public class TabsPanel extends LinearLayout
|
|||
return mActivity.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private static int getPanelsContainerHeight(PanelViewContainer panelsContainer) {
|
||||
Resources resources = panelsContainer.getContext().getResources();
|
||||
private static int getTabContainerHeight(TabsLayoutContainer tabsContainer) {
|
||||
Resources resources = tabsContainer.getContext().getResources();
|
||||
|
||||
PanelView panelView = panelsContainer.getCurrentPanelView();
|
||||
PanelView panelView = tabsContainer.getCurrentPanelView();
|
||||
if (panelView != null && !panelView.shouldExpand()) {
|
||||
return resources.getDimensionPixelSize(R.dimen.tabs_tray_horizontal_height);
|
||||
return resources.getDimensionPixelSize(R.dimen.tabs_layout_horizontal_height);
|
||||
}
|
||||
|
||||
int actionBarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height);
|
||||
int screenHeight = resources.getDisplayMetrics().heightPixels;
|
||||
|
||||
Rect windowRect = new Rect();
|
||||
panelsContainer.getWindowVisibleDisplayFrame(windowRect);
|
||||
tabsContainer.getWindowVisibleDisplayFrame(windowRect);
|
||||
int windowHeight = windowRect.bottom - windowRect.top;
|
||||
|
||||
// The web content area should have at least 1.5x the height of the action bar.
|
||||
|
@ -338,9 +338,8 @@ public class TabsPanel extends LinearLayout
|
|||
onLightweightThemeChanged();
|
||||
}
|
||||
|
||||
// Panel View Container holds the ListView
|
||||
static class PanelViewContainer extends FrameLayout {
|
||||
public PanelViewContainer(Context context, AttributeSet attrs) {
|
||||
static class TabsLayoutContainer extends FrameLayout {
|
||||
public TabsLayoutContainer(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
|
@ -361,7 +360,7 @@ public class TabsPanel extends LinearLayout
|
|||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
if (!GeckoAppShell.getGeckoInterface().hasTabsSideBar()) {
|
||||
int heightSpec = MeasureSpec.makeMeasureSpec(getPanelsContainerHeight(PanelViewContainer.this), MeasureSpec.EXACTLY);
|
||||
int heightSpec = MeasureSpec.makeMeasureSpec(getTabContainerHeight(TabsLayoutContainer.this), MeasureSpec.EXACTLY);
|
||||
super.onMeasure(widthMeasureSpec, heightSpec);
|
||||
} else {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
@ -483,7 +482,7 @@ public class TabsPanel extends LinearLayout
|
|||
dispatchLayoutChange(getWidth(), getHeight());
|
||||
} else {
|
||||
int actionBarHeight = mContext.getResources().getDimensionPixelSize(R.dimen.browser_toolbar_height);
|
||||
int height = actionBarHeight + getPanelsContainerHeight(mPanelsContainer);
|
||||
int height = actionBarHeight + getTabContainerHeight(mTabsContainer);
|
||||
dispatchLayoutChange(getWidth(), height);
|
||||
}
|
||||
mHeaderVisible = true;
|
||||
|
@ -502,7 +501,7 @@ public class TabsPanel extends LinearLayout
|
|||
public void refresh() {
|
||||
removeAllViews();
|
||||
|
||||
LayoutInflater.from(mContext).inflate(R.layout.tabs_panel, this);
|
||||
inflateLayout(mContext);
|
||||
initialize();
|
||||
|
||||
if (mVisible)
|
||||
|
@ -541,13 +540,13 @@ public class TabsPanel extends LinearLayout
|
|||
final int tabsPanelWidth = getWidth();
|
||||
if (mVisible) {
|
||||
ViewHelper.setTranslationX(mHeader, -tabsPanelWidth);
|
||||
ViewHelper.setTranslationX(mPanelsContainer, -tabsPanelWidth);
|
||||
ViewHelper.setTranslationX(mTabsContainer, -tabsPanelWidth);
|
||||
|
||||
// The footer view is only present on the sidebar, v11+.
|
||||
ViewHelper.setTranslationX(mFooter, -tabsPanelWidth);
|
||||
}
|
||||
final int translationX = (mVisible ? 0 : -tabsPanelWidth);
|
||||
animator.attach(mPanelsContainer, PropertyAnimator.Property.TRANSLATION_X, translationX);
|
||||
animator.attach(mTabsContainer, PropertyAnimator.Property.TRANSLATION_X, translationX);
|
||||
animator.attach(mHeader, PropertyAnimator.Property.TRANSLATION_X, translationX);
|
||||
animator.attach(mFooter, PropertyAnimator.Property.TRANSLATION_X, translationX);
|
||||
|
||||
|
@ -557,16 +556,16 @@ public class TabsPanel extends LinearLayout
|
|||
final int translationY = (mVisible ? 0 : -toolbarHeight);
|
||||
if (mVisible) {
|
||||
ViewHelper.setTranslationY(mHeader, -toolbarHeight);
|
||||
ViewHelper.setTranslationY(mPanelsContainer, -toolbarHeight);
|
||||
ViewHelper.setAlpha(mPanelsContainer, 0.0f);
|
||||
ViewHelper.setTranslationY(mTabsContainer, -toolbarHeight);
|
||||
ViewHelper.setAlpha(mTabsContainer, 0.0f);
|
||||
}
|
||||
animator.attach(mPanelsContainer, PropertyAnimator.Property.ALPHA, mVisible ? 1.0f : 0.0f);
|
||||
animator.attach(mPanelsContainer, PropertyAnimator.Property.TRANSLATION_Y, translationY);
|
||||
animator.attach(mTabsContainer, PropertyAnimator.Property.ALPHA, mVisible ? 1.0f : 0.0f);
|
||||
animator.attach(mTabsContainer, PropertyAnimator.Property.TRANSLATION_Y, translationY);
|
||||
animator.attach(mHeader, PropertyAnimator.Property.TRANSLATION_Y, translationY);
|
||||
}
|
||||
|
||||
mHeader.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
mPanelsContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
mTabsContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
}
|
||||
|
||||
public void finishTabsAnimation() {
|
||||
|
@ -575,10 +574,10 @@ public class TabsPanel extends LinearLayout
|
|||
}
|
||||
|
||||
mHeader.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
mPanelsContainer.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
mTabsContainer.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
|
||||
// If the tray is now hidden, call hide() on current panel and unset it as the current panel
|
||||
// to avoid hide() being called again when the tray is opened next.
|
||||
// If the tabs panel is now hidden, call hide() on current panel and unset it as the current panel
|
||||
// to avoid hide() being called again when the layout is opened next.
|
||||
if (!mVisible && mPanel != null) {
|
||||
mPanel.hide();
|
||||
mPanel = null;
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.mozilla.gecko.GeckoEvent;
|
|||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.GeckoThread;
|
||||
import org.mozilla.gecko.GeckoThread.LaunchState;
|
||||
import org.mozilla.gecko.NewTabletUI;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.RobocopUtils;
|
||||
import org.mozilla.gecko.Tab;
|
||||
|
@ -69,6 +70,8 @@ abstract class BaseTest extends BaseRobocopTest {
|
|||
private static final int GECKO_READY_WAIT_MS = 180000;
|
||||
public static final int MAX_WAIT_BLOCK_FOR_EVENT_DATA_MS = 90000;
|
||||
|
||||
private static final String URL_HTTP_PREFIX = "http://";
|
||||
|
||||
private Activity mActivity;
|
||||
private int mPreferenceRequestID = 0;
|
||||
protected Solo mSolo;
|
||||
|
@ -527,7 +530,25 @@ abstract class BaseTest extends BaseRobocopTest {
|
|||
}
|
||||
}
|
||||
|
||||
public final void verifyPageTitle(String title) {
|
||||
public final void verifyPageTitle(final String title, String url) {
|
||||
// We are asserting visible state - we shouldn't know if the title is null.
|
||||
mAsserter.isnot(title, null, "The title argument is not null");
|
||||
mAsserter.isnot(url, null, "The url argument is not null");
|
||||
|
||||
// TODO: We should also check the title bar preference.
|
||||
final String expected;
|
||||
if (!NewTabletUI.isEnabled(mActivity)) {
|
||||
expected = title;
|
||||
} else {
|
||||
if (StringHelper.ABOUT_HOME_URL.equals(url)) {
|
||||
expected = StringHelper.ABOUT_HOME_TITLE;
|
||||
} else if (url.startsWith(URL_HTTP_PREFIX)) {
|
||||
expected = url.substring(URL_HTTP_PREFIX.length());
|
||||
} else {
|
||||
expected = url;
|
||||
}
|
||||
}
|
||||
|
||||
final TextView urlBarTitle = (TextView) mSolo.getView(R.id.url_bar_title);
|
||||
String pageTitle = null;
|
||||
if (urlBarTitle != null) {
|
||||
|
@ -536,7 +557,7 @@ abstract class BaseTest extends BaseRobocopTest {
|
|||
waitForCondition(new VerifyTextViewText(urlBarTitle, title), MAX_WAIT_VERIFY_PAGE_TITLE_MS);
|
||||
pageTitle = urlBarTitle.getText().toString();
|
||||
}
|
||||
mAsserter.is(pageTitle, title, "Page title is correct");
|
||||
mAsserter.is(pageTitle, expected, "Page title is correct");
|
||||
}
|
||||
|
||||
public final void verifyTabCount(int expectedTabCount) {
|
||||
|
@ -619,23 +640,23 @@ abstract class BaseTest extends BaseRobocopTest {
|
|||
/**
|
||||
* Gets the AdapterView of the tabs list.
|
||||
*
|
||||
* @return List view in the tabs tray
|
||||
* @return List view in the tabs panel
|
||||
*/
|
||||
private final AdapterView<ListAdapter> getTabsList() {
|
||||
private final AdapterView<ListAdapter> getTabsLayout() {
|
||||
Element tabs = mDriver.findElement(getActivity(), R.id.tabs);
|
||||
tabs.click();
|
||||
return (AdapterView<ListAdapter>) getActivity().findViewById(R.id.normal_tabs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the view in the tabs tray at the specified index.
|
||||
* Gets the view in the tabs panel at the specified index.
|
||||
*
|
||||
* @return View at index
|
||||
*/
|
||||
private View getTabViewAt(final int index) {
|
||||
final View[] childView = { null };
|
||||
|
||||
final AdapterView<ListAdapter> view = getTabsList();
|
||||
final AdapterView<ListAdapter> view = getTabsLayout();
|
||||
|
||||
runOnUiThreadSync(new Runnable() {
|
||||
@Override
|
||||
|
|
|
@ -197,7 +197,7 @@ public abstract class SessionTest extends BaseTest {
|
|||
verifyUrl(page.url);
|
||||
} else {
|
||||
waitForText(page.title);
|
||||
verifyPageTitle(page.title);
|
||||
verifyPageTitle(page.title, page.url);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,9 +9,12 @@ import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertFalse;
|
|||
import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertNotNull;
|
||||
import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertTrue;
|
||||
|
||||
import org.mozilla.gecko.NewTabletUI;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.tests.StringHelper;
|
||||
import org.mozilla.gecko.tests.UITestContext;
|
||||
import org.mozilla.gecko.tests.helpers.DeviceHelper;
|
||||
import org.mozilla.gecko.tests.helpers.NavigationHelper;
|
||||
import org.mozilla.gecko.tests.helpers.WaitHelper;
|
||||
|
||||
import android.view.View;
|
||||
|
@ -26,6 +29,9 @@ import com.jayway.android.robotium.solo.Solo;
|
|||
* A class representing any interactions that take place on the Toolbar.
|
||||
*/
|
||||
public class ToolbarComponent extends BaseComponent {
|
||||
|
||||
private static final String URL_HTTP_PREFIX = "http://";
|
||||
|
||||
public ToolbarComponent(final UITestContext testContext) {
|
||||
super(testContext);
|
||||
}
|
||||
|
@ -40,7 +46,26 @@ public class ToolbarComponent extends BaseComponent {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ToolbarComponent assertTitle(final String expected) {
|
||||
public ToolbarComponent assertTitle(final String title, final String url) {
|
||||
// We are asserting visible state - we shouldn't know if the title is null.
|
||||
fAssertNotNull("The title argument is not null", title);
|
||||
fAssertNotNull("The url argument is not null", url);
|
||||
|
||||
// TODO: We should also check the title bar preference.
|
||||
final String expected;
|
||||
if (!NewTabletUI.isEnabled(mActivity)) {
|
||||
expected = title;
|
||||
} else {
|
||||
final String absoluteURL = NavigationHelper.adjustUrl(url);
|
||||
if (StringHelper.ABOUT_HOME_URL.equals(absoluteURL)) {
|
||||
expected = StringHelper.ABOUT_HOME_TITLE;
|
||||
} else if (absoluteURL.startsWith(URL_HTTP_PREFIX)) {
|
||||
expected = absoluteURL.substring(URL_HTTP_PREFIX.length());
|
||||
} else {
|
||||
expected = absoluteURL;
|
||||
}
|
||||
}
|
||||
|
||||
fAssertEquals("The Toolbar title is " + expected, expected, getTitle());
|
||||
return this;
|
||||
}
|
||||
|
@ -91,7 +116,7 @@ public class ToolbarComponent extends BaseComponent {
|
|||
return (ImageButton) getToolbarView().findViewById(R.id.edit_cancel);
|
||||
}
|
||||
|
||||
private CharSequence getTitle() {
|
||||
private String getTitle() {
|
||||
return getTitleHelper(true);
|
||||
}
|
||||
|
||||
|
@ -100,16 +125,16 @@ public class ToolbarComponent extends BaseComponent {
|
|||
* may return a value that may never be visible to the user. Callers likely want to use
|
||||
* {@link assertTitle} instead.
|
||||
*/
|
||||
public CharSequence getPotentiallyInconsistentTitle() {
|
||||
public String getPotentiallyInconsistentTitle() {
|
||||
return getTitleHelper(false);
|
||||
}
|
||||
|
||||
private CharSequence getTitleHelper(final boolean shouldAssertNotEditing) {
|
||||
private String getTitleHelper(final boolean shouldAssertNotEditing) {
|
||||
if (shouldAssertNotEditing) {
|
||||
assertIsNotEditing();
|
||||
}
|
||||
|
||||
return getUrlTitleText().getText();
|
||||
return getUrlTitleText().getText().toString();
|
||||
}
|
||||
|
||||
private boolean isEditing() {
|
||||
|
|
|
@ -44,7 +44,7 @@ final public class NavigationHelper {
|
|||
/**
|
||||
* Returns a new URL with the docshell HTTP server host prefix.
|
||||
*/
|
||||
private static String adjustUrl(final String url) {
|
||||
public static String adjustUrl(final String url) {
|
||||
fAssertNotNull("url is not null", url);
|
||||
|
||||
if (url.startsWith("about:") || url.startsWith("chrome:")) {
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
#[testTabHistory]
|
||||
# fails on gs2, nexus one, lg revolution, nexus s
|
||||
|
||||
#[testTabsTrayMenu]
|
||||
#[testTabsLayoutMenu]
|
||||
# fails on gs2, nexus one, lg revolution, nexus s
|
||||
|
||||
#[testThumbnails]
|
||||
|
|
|
@ -13,18 +13,20 @@ public class testAboutHomeVisibility extends UITest {
|
|||
GeckoHelper.blockForReady();
|
||||
|
||||
// Check initial state on about:home.
|
||||
mToolbar.assertTitle(StringHelper.ABOUT_HOME_TITLE);
|
||||
mToolbar.assertTitle(StringHelper.ABOUT_HOME_TITLE, StringHelper.ABOUT_HOME_URL);
|
||||
mAboutHome.assertVisible()
|
||||
.assertCurrentPanel(PanelType.TOP_SITES);
|
||||
|
||||
// Go to blank 01.
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE,
|
||||
StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
mAboutHome.assertNotVisible();
|
||||
|
||||
// Go to blank 02.
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_BLANK_PAGE_02_URL);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE,
|
||||
StringHelper.ROBOCOP_BLANK_PAGE_02_URL);
|
||||
mAboutHome.assertNotVisible();
|
||||
|
||||
// Enter editing mode, where the about:home UI should be visible.
|
||||
|
@ -38,7 +40,7 @@ public class testAboutHomeVisibility extends UITest {
|
|||
|
||||
// Loading about:home should show about:home again.
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.ABOUT_HOME_URL);
|
||||
mToolbar.assertTitle(StringHelper.ABOUT_HOME_TITLE);
|
||||
mToolbar.assertTitle(StringHelper.ABOUT_HOME_TITLE, StringHelper.ABOUT_HOME_URL);
|
||||
mAboutHome.assertVisible()
|
||||
.assertCurrentPanel(PanelType.TOP_SITES);
|
||||
|
||||
|
@ -49,7 +51,5 @@ public class testAboutHomeVisibility extends UITest {
|
|||
mAboutHome.navigateToBuiltinPanelType(PanelType.HISTORY)
|
||||
.assertVisible()
|
||||
.assertCurrentPanel(PanelType.HISTORY);
|
||||
|
||||
// TODO: Type in a url and assert the go button is visible.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,34 +2,44 @@ package org.mozilla.gecko.tests;
|
|||
|
||||
import org.mozilla.gecko.Actions;
|
||||
import org.mozilla.gecko.Element;
|
||||
import org.mozilla.gecko.NewTabletUI;
|
||||
import org.mozilla.gecko.R;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
/* Tests related to the about: page:
|
||||
* - check that about: loads from the URL bar
|
||||
* - check that about: loads from Settings/About...
|
||||
*/
|
||||
public class testAboutPage extends PixelTest {
|
||||
private void ensureTitleMatches(final String regex) {
|
||||
Element urlBarTitle = mDriver.findElement(getActivity(), R.id.url_bar_title);
|
||||
/**
|
||||
* Ensures the page title matches the given regex (as opposed to String equality).
|
||||
*/
|
||||
private void ensureTitleMatches(final String titleRegex, final String urlRegex) {
|
||||
final Activity activity = getActivity();
|
||||
final Element urlBarTitle = mDriver.findElement(activity, R.id.url_bar_title);
|
||||
|
||||
// TODO: We should also be testing what the page title preference value is.
|
||||
final String expectedTitle = NewTabletUI.isEnabled(activity) ? urlRegex : titleRegex;
|
||||
mAsserter.isnot(urlBarTitle, null, "Got the URL bar title");
|
||||
assertMatches(urlBarTitle.getText(), regex, "page title match");
|
||||
assertMatches(urlBarTitle.getText(), expectedTitle, "page title match");
|
||||
}
|
||||
|
||||
public void testAboutPage() {
|
||||
blockForGeckoReady();
|
||||
|
||||
// Load the about: page and verify its title.
|
||||
String url = "about:";
|
||||
String url = StringHelper.ABOUT_SCHEME;
|
||||
loadAndPaint(url);
|
||||
|
||||
ensureTitleMatches(StringHelper.ABOUT_LABEL);
|
||||
ensureTitleMatches(StringHelper.ABOUT_LABEL, url);
|
||||
|
||||
// Open a new page to remove the about: page from the current tab.
|
||||
url = getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
inputAndLoadUrl(url);
|
||||
|
||||
// At this point the page title should have been set.
|
||||
ensureTitleMatches(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE, url);
|
||||
|
||||
// Set up listeners to catch the page load we're about to do.
|
||||
Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added");
|
||||
|
@ -45,6 +55,6 @@ public class testAboutPage extends PixelTest {
|
|||
contentEventExpecter.unregisterListener();
|
||||
|
||||
// Grab the title to make sure the about: page was loaded.
|
||||
ensureTitleMatches(StringHelper.ABOUT_LABEL);
|
||||
ensureTitleMatches(StringHelper.ABOUT_LABEL, StringHelper.ABOUT_SCHEME);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ public class testAddSearchEngine extends AboutHomeTest {
|
|||
// Load the page for the search engine to add.
|
||||
inputAndLoadUrl(searchEngineURL);
|
||||
waitForText(StringHelper.ROBOCOP_SEARCH_TITLE);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_SEARCH_TITLE);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_SEARCH_TITLE, searchEngineURL);
|
||||
|
||||
// Used to long-tap on the search input box for the search engine to add.
|
||||
getInstrumentation().waitForIdleSync();
|
||||
|
@ -99,7 +99,7 @@ public class testAddSearchEngine extends AboutHomeTest {
|
|||
|
||||
mAsserter.dumpLog("Search Engines list = " + searchEngines.toString());
|
||||
mAsserter.is(searchEngines.size(), initialNumSearchEngines + 1, "Checking the number of Search Engines has increased");
|
||||
|
||||
|
||||
// Verify that the number of displayed searchengines is the same as the one received through the SearchEngines:Data event.
|
||||
verifyDisplayedSearchEnginesCount(initialNumSearchEngines + 1);
|
||||
searchEngineDataEventExpector.unregisterListener();
|
||||
|
@ -151,7 +151,7 @@ public class testAddSearchEngine extends AboutHomeTest {
|
|||
return (adapter.getCount() == expectedCount);
|
||||
}
|
||||
}, MAX_WAIT_TEST_MS);
|
||||
|
||||
|
||||
// Exit about:home
|
||||
mActions.sendSpecialKey(Actions.SpecialKey.BACK);
|
||||
waitForText(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
|
||||
|
|
|
@ -17,7 +17,7 @@ public class testAddonManager extends PixelTest {
|
|||
public void testAddonManager() {
|
||||
Actions.EventExpecter tabEventExpecter;
|
||||
Actions.EventExpecter contentEventExpecter;
|
||||
String url = StringHelper.ABOUT_ADDONS_URL;
|
||||
final String aboutAddonsURL = StringHelper.ABOUT_ADDONS_URL;
|
||||
|
||||
blockForGeckoReady();
|
||||
|
||||
|
@ -36,21 +36,22 @@ public class testAddonManager extends PixelTest {
|
|||
contentEventExpecter.unregisterListener();
|
||||
|
||||
// Verify the url
|
||||
verifyPageTitle(StringHelper.ADDONS_LABEL);
|
||||
verifyPageTitle(StringHelper.ADDONS_LABEL, aboutAddonsURL);
|
||||
|
||||
// Close the Add-on Manager
|
||||
mActions.sendSpecialKey(Actions.SpecialKey.BACK);
|
||||
|
||||
// Load the about:addons page and verify it was loaded
|
||||
loadAndPaint(url);
|
||||
verifyPageTitle(StringHelper.ADDONS_LABEL);
|
||||
loadAndPaint(aboutAddonsURL);
|
||||
verifyPageTitle(StringHelper.ADDONS_LABEL, aboutAddonsURL);
|
||||
|
||||
// Setup wait for tab to spawn and load
|
||||
tabEventExpecter = mActions.expectGeckoEvent("Tab:Added");
|
||||
contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
|
||||
|
||||
// Open a new tab
|
||||
addTab(getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL));
|
||||
final String blankURL = getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
addTab(blankURL);
|
||||
|
||||
// Wait for the new tab and page to load
|
||||
tabEventExpecter.blockForEvent();
|
||||
|
@ -63,7 +64,7 @@ public class testAddonManager extends PixelTest {
|
|||
verifyTabCount(2);
|
||||
|
||||
// Verify the page was opened
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE, blankURL);
|
||||
|
||||
// Addons Manager is not opened 2 separate times when opened from the menu
|
||||
selectMenuItem(StringHelper.ADDONS_LABEL);
|
||||
|
|
|
@ -48,7 +48,8 @@ public class testAppMenuPathways extends UITest {
|
|||
// The above mock video playback test changes Java state, but not the associated JS state.
|
||||
// Navigate to a new page so that the Java state is cleared.
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE,
|
||||
StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
|
||||
// Test save as pdf functionality.
|
||||
// The following call doesn't wait for the resulting pdf but checks that no exception are thrown.
|
||||
|
|
|
@ -32,7 +32,8 @@ public class testBookmark extends AboutHomeTest {
|
|||
isBookmarkDisplayed(BOOKMARK_URL);
|
||||
loadBookmark(BOOKMARK_URL);
|
||||
waitForText(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE,
|
||||
StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
|
||||
mDatabaseHelper.deleteBookmark(BOOKMARK_URL);
|
||||
waitForBookmarked(false);
|
||||
|
|
|
@ -52,7 +52,7 @@ public class testBookmarkFolders extends AboutHomeTest {
|
|||
// Open the bookmark from a bookmark folder hierarchy
|
||||
loadBookmark(DESKTOP_BOOKMARK_URL);
|
||||
waitForText(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE, DESKTOP_BOOKMARK_URL);
|
||||
openAboutHomeTab(AboutHomeTabs.BOOKMARKS);
|
||||
|
||||
// Check that folders don't have a context menu
|
||||
|
|
|
@ -19,7 +19,8 @@ public class testBookmarkKeyword extends AboutHomeTest {
|
|||
waitForText(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
|
||||
|
||||
// Make sure the title of the page appeared.
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE,
|
||||
StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
|
||||
// Delete the bookmark to clean up.
|
||||
mDatabaseHelper.deleteBookmark(url);
|
||||
|
|
|
@ -18,7 +18,9 @@ public class testBookmarklets extends AboutHomeTest {
|
|||
|
||||
// load a standard page so bookmarklets work
|
||||
inputAndLoadUrl(url);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE); // Waiting for page title to ensure the page is loaded
|
||||
// Waiting for page title to ensure the page is loaded
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE,
|
||||
StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
|
||||
// verify that user-entered bookmarklets do *not* work
|
||||
enterUrl(js);
|
||||
|
|
|
@ -30,14 +30,14 @@ public class testClearPrivateData extends PixelTest {
|
|||
String blank2 = getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_02_URL);
|
||||
String title = StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE;
|
||||
inputAndLoadUrl(blank1);
|
||||
verifyPageTitle(title);
|
||||
verifyPageTitle(title, blank1);
|
||||
mDatabaseHelper.addOrUpdateMobileBookmark(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE, blank2);
|
||||
|
||||
// Checking that the history list is not empty
|
||||
verifyHistoryCount(1);
|
||||
|
||||
//clear and check for device
|
||||
checkDevice(title);
|
||||
checkDevice(title, blank1);
|
||||
|
||||
// Checking that history list is empty
|
||||
verifyHistoryCount(0);
|
||||
|
@ -65,7 +65,7 @@ public class testClearPrivateData extends PixelTest {
|
|||
checkOption(shareStrings[3], "Cancel");
|
||||
loadCheckDismiss(shareStrings[2], url, shareStrings[0]);
|
||||
checkOption(shareStrings[2], "Cancel");
|
||||
checkDevice(titleGeolocation);
|
||||
checkDevice(titleGeolocation, url);
|
||||
}
|
||||
|
||||
public void clearPassword(){
|
||||
|
@ -75,24 +75,20 @@ public class testClearPrivateData extends PixelTest {
|
|||
loadCheckDismiss(passwordStrings[1], loginUrl, passwordStrings[0]);
|
||||
checkOption(passwordStrings[1], "Clear");
|
||||
loadCheckDismiss(passwordStrings[2], loginUrl, passwordStrings[0]);
|
||||
checkDevice(title);
|
||||
checkDevice(title, getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL));
|
||||
}
|
||||
|
||||
// clear private data and verify the device type because for phone there is an extra back action to exit the settings menu
|
||||
public void checkDevice(String title) {
|
||||
public void checkDevice(final String title, final String url) {
|
||||
clearPrivateData();
|
||||
if (mDevice.type.equals("phone")) {
|
||||
mActions.sendSpecialKey(Actions.SpecialKey.BACK);
|
||||
mAsserter.ok(waitForText(StringHelper.PRIVACY_SECTION_LABEL), "waiting to perform one back", "one back");
|
||||
mActions.sendSpecialKey(Actions.SpecialKey.BACK);
|
||||
verifyPageTitle(title);
|
||||
}
|
||||
else {
|
||||
mActions.sendSpecialKey(Actions.SpecialKey.BACK);
|
||||
verifyPageTitle(title);
|
||||
}
|
||||
mActions.sendSpecialKey(Actions.SpecialKey.BACK);
|
||||
verifyPageTitle(title, url);
|
||||
}
|
||||
|
||||
|
||||
// Load a URL, verify that the doorhanger appears and dismiss it
|
||||
public void loadCheckDismiss(String option, String url, String message) {
|
||||
inputAndLoadUrl(url);
|
||||
|
|
|
@ -17,11 +17,11 @@ public class testHistory extends AboutHomeTest {
|
|||
String url3 = getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_03_URL);
|
||||
|
||||
inputAndLoadUrl(url);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_URL, url);
|
||||
inputAndLoadUrl(url2);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_02_URL);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_02_URL, url2);
|
||||
inputAndLoadUrl(url3);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_03_URL);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_03_URL, url3);
|
||||
|
||||
openAboutHomeTab(AboutHomeTabs.HISTORY);
|
||||
|
||||
|
@ -62,7 +62,7 @@ public class testHistory extends AboutHomeTest {
|
|||
|
||||
// The first item here (since it was just visited) should be a "Switch to tab" item
|
||||
// i.e. don't expect a DOMContentLoaded event
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_03_URL);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_BLANK_PAGE_03_URL, StringHelper.ROBOCOP_BLANK_PAGE_03_URL);
|
||||
verifyUrl(url3);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,8 +24,9 @@ public class testInputConnection extends UITest {
|
|||
public void testInputConnection() throws InterruptedException {
|
||||
GeckoHelper.blockForReady();
|
||||
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_INPUT_URL + "#" + INITIAL_TEXT);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_INPUT_TITLE);
|
||||
final String url = StringHelper.ROBOCOP_INPUT_URL + "#" + INITIAL_TEXT;
|
||||
NavigationHelper.enterAndLoadUrl(url);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_INPUT_TITLE, url);
|
||||
|
||||
mGeckoView.mTextInput
|
||||
.waitForInputConnection()
|
||||
|
|
|
@ -18,7 +18,7 @@ public class testPictureLinkContextMenu extends ContentContextMenuTest {
|
|||
PICTURE_PAGE_URL=getAbsoluteUrl(StringHelper.ROBOCOP_PICTURE_LINK_URL);
|
||||
BLANK_PAGE_URL=getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_02_URL);
|
||||
loadAndPaint(PICTURE_PAGE_URL);
|
||||
verifyPageTitle(PICTURE_PAGE_TITLE);
|
||||
verifyPageTitle(PICTURE_PAGE_TITLE, PICTURE_PAGE_URL);
|
||||
|
||||
switchTabs(imageTitle);
|
||||
verifyContextMenuItems(photoMenuItems);
|
||||
|
|
|
@ -73,7 +73,7 @@ public class testReaderMode extends AboutHomeTest {
|
|||
contentPageShowExpecter.unregisterListener();
|
||||
paintExpecter.blockUntilClear(EVENT_CLEAR_DELAY_MS);
|
||||
paintExpecter.unregisterListener();
|
||||
verifyPageTitle(StringHelper.ROBOCOP_TEXT_PAGE_TITLE);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_TEXT_PAGE_TITLE, StringHelper.ROBOCOP_TEXT_PAGE_URL);
|
||||
|
||||
// Open the share menu for the reader toolbar
|
||||
height = mDriver.getGeckoTop() + mDriver.getGeckoHeight() - 10;
|
||||
|
@ -134,7 +134,7 @@ public class testReaderMode extends AboutHomeTest {
|
|||
mSolo.clickOnView(child);
|
||||
contentEventExpecter.blockForEvent();
|
||||
contentEventExpecter.unregisterListener();
|
||||
verifyPageTitle(StringHelper.ROBOCOP_TEXT_PAGE_TITLE);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_TEXT_PAGE_TITLE, StringHelper.ROBOCOP_TEXT_PAGE_URL);
|
||||
|
||||
// Verify that we are in reader mode and remove the page from Reading List
|
||||
height = mDriver.getGeckoTop() + mDriver.getGeckoHeight() - 10;
|
||||
|
@ -142,7 +142,7 @@ public class testReaderMode extends AboutHomeTest {
|
|||
mAsserter.dumpLog("Long Clicking at width = " + String.valueOf(width) + " and height = " + String.valueOf(height));
|
||||
mSolo.clickOnScreen(width,height);
|
||||
mAsserter.ok(mSolo.waitForText("Page removed from your Reading List"), "Waiting for the page to removed from your Reading List", "The page is removed from your Reading List");
|
||||
verifyPageTitle(StringHelper.ROBOCOP_TEXT_PAGE_TITLE);
|
||||
verifyPageTitle(StringHelper.ROBOCOP_TEXT_PAGE_TITLE, StringHelper.ROBOCOP_TEXT_PAGE_URL);
|
||||
|
||||
//Check if the Reading List is empty
|
||||
openAboutHomeTab(AboutHomeTabs.READING_LIST);
|
||||
|
|
|
@ -16,8 +16,9 @@ public class testSelectionHandler extends UITest {
|
|||
GeckoHelper.blockForReady();
|
||||
|
||||
Actions.EventExpecter robocopTestExpecter = getActions().expectGeckoEvent("Robocop:testSelectionHandler");
|
||||
NavigationHelper.enterAndLoadUrl("chrome://roboextender/content/testSelectionHandler.html");
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_SELECTION_HANDLER_TITLE);
|
||||
final String url = "chrome://roboextender/content/testSelectionHandler.html";
|
||||
NavigationHelper.enterAndLoadUrl(url);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_SELECTION_HANDLER_TITLE, url);
|
||||
|
||||
while (!test(robocopTestExpecter)) {
|
||||
// do nothing
|
||||
|
|
|
@ -10,25 +10,32 @@ public class testSessionHistory extends UITest {
|
|||
public void testSessionHistory() {
|
||||
GeckoHelper.blockForReady();
|
||||
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
|
||||
String url = StringHelper.ROBOCOP_BLANK_PAGE_01_URL;
|
||||
NavigationHelper.enterAndLoadUrl(url);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE, url);
|
||||
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_BLANK_PAGE_02_URL);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE);
|
||||
url = StringHelper.ROBOCOP_BLANK_PAGE_02_URL;
|
||||
NavigationHelper.enterAndLoadUrl(url);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE, url);
|
||||
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_BLANK_PAGE_03_URL);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_03_TITLE);
|
||||
url = StringHelper.ROBOCOP_BLANK_PAGE_03_URL;
|
||||
NavigationHelper.enterAndLoadUrl(url);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_03_TITLE, url);
|
||||
|
||||
NavigationHelper.goBack();
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE,
|
||||
StringHelper.ROBOCOP_BLANK_PAGE_02_URL);
|
||||
|
||||
NavigationHelper.goBack();
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE,
|
||||
StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
|
||||
NavigationHelper.goForward();
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE,
|
||||
StringHelper.ROBOCOP_BLANK_PAGE_02_URL);
|
||||
|
||||
NavigationHelper.reload();
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE);
|
||||
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE,
|
||||
StringHelper.ROBOCOP_BLANK_PAGE_02_URL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ public class testShareLink extends AboutHomeTest {
|
|||
openAboutHomeTab(AboutHomeTabs.READING_LIST);
|
||||
|
||||
inputAndLoadUrl(url);
|
||||
verifyPageTitle(urlTitle); // Waiting for page title to ensure the page is loaded
|
||||
verifyPageTitle(urlTitle, url); // Waiting for page title to ensure the page is loaded
|
||||
|
||||
selectMenuItem(StringHelper.SHARE_LABEL);
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
|
@ -248,7 +248,7 @@ public class testShareLink extends AboutHomeTest {
|
|||
public boolean test() {
|
||||
ArrayList<View> views = mSolo.getCurrentViews();
|
||||
for (View view : views) {
|
||||
// List may be displayed in different view formats.
|
||||
// List may be displayed in different view formats.
|
||||
// On JB, GridView is common; on ICS-, ListView is common.
|
||||
if (view instanceof ListView ||
|
||||
view instanceof GridView) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.Actions;
|
||||
import org.mozilla.gecko.NewTabletUI;
|
||||
|
||||
/**
|
||||
* This patch tests the option that shows the full URL and title in the URL Bar
|
||||
|
@ -7,6 +9,11 @@ import org.mozilla.gecko.Actions;
|
|||
|
||||
public class testTitleBar extends PixelTest {
|
||||
public void testTitleBar() {
|
||||
// Because there is no title bar option on new tablet, we don't need to run this test.
|
||||
if (NewTabletUI.isEnabled(getActivity())) {
|
||||
return;
|
||||
}
|
||||
|
||||
blockForGeckoReady();
|
||||
checkOption();
|
||||
}
|
||||
|
@ -18,7 +25,7 @@ public class testTitleBar extends PixelTest {
|
|||
|
||||
// Loading a page
|
||||
inputAndLoadUrl(blank1);
|
||||
verifyPageTitle(title);
|
||||
verifyPageTitle(title, blank1);
|
||||
|
||||
// Ensure the full URL is displayed in the URL Bar
|
||||
selectOption(StringHelper.SHOW_PAGE_ADDRESS_LABEL);
|
||||
|
@ -28,7 +35,7 @@ public class testTitleBar extends PixelTest {
|
|||
// Ensure the title is displayed in the URL Bar
|
||||
selectOption(StringHelper.SHOW_PAGE_TITLE_LABEL);
|
||||
inputAndLoadUrl(blank1);
|
||||
verifyPageTitle(title);
|
||||
verifyPageTitle(title, blank1);
|
||||
}
|
||||
|
||||
// Entering settings, changing the options: show title/page address option and verifing the device type because for phone there is an extra back action to exit the settings menu
|
||||
|
|
|
@ -36,6 +36,8 @@ public class TabCounter extends ThemedTextSwitcher
|
|||
private final int mLayoutId;
|
||||
|
||||
private int mCount;
|
||||
public static final int MAX_VISIBLE_TABS = 99;
|
||||
public static final String SO_MANY_TABS_OPEN = "∞";
|
||||
|
||||
private enum FadeMode {
|
||||
FADE_IN,
|
||||
|
@ -84,6 +86,12 @@ public class TabCounter extends ThemedTextSwitcher
|
|||
return;
|
||||
}
|
||||
|
||||
// don't animate if there are still over MAX_VISIBLE_TABS tabs open
|
||||
if (mCount > MAX_VISIBLE_TABS && count > MAX_VISIBLE_TABS) {
|
||||
mCount = count;
|
||||
return;
|
||||
}
|
||||
|
||||
if (count < mCount) {
|
||||
setInAnimation(mFlipInBackward);
|
||||
setOutAnimation(mFlipOutForward);
|
||||
|
@ -97,14 +105,21 @@ public class TabCounter extends ThemedTextSwitcher
|
|||
setDisplayedChild(0);
|
||||
|
||||
// Set In value, trigger animation to Out value
|
||||
setCurrentText(String.valueOf(mCount));
|
||||
setText(String.valueOf(count));
|
||||
setCurrentText(formatForDisplay(mCount));
|
||||
setText(formatForDisplay(count));
|
||||
|
||||
mCount = count;
|
||||
}
|
||||
|
||||
private String formatForDisplay(int count) {
|
||||
if (count > MAX_VISIBLE_TABS) {
|
||||
return SO_MANY_TABS_OPEN;
|
||||
}
|
||||
return String.valueOf(count);
|
||||
}
|
||||
|
||||
void setCount(int count) {
|
||||
setCurrentText(String.valueOf(count));
|
||||
setCurrentText(formatForDisplay(count));
|
||||
mCount = count;
|
||||
}
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ public class IconTabWidget extends TabWidget {
|
|||
if (!mIsIcon) {
|
||||
return null;
|
||||
}
|
||||
// We can have multiple views in the tray for each child. This finds the
|
||||
// We can have multiple views in the tabs panel for each child. This finds the
|
||||
// first view corresponding to the given tab. This varies by Android
|
||||
// version. The first view should always be our ImageButton, but let's
|
||||
// be safe.
|
||||
|
@ -98,7 +98,7 @@ public class IconTabWidget extends TabWidget {
|
|||
if (!mIsIcon) {
|
||||
return;
|
||||
}
|
||||
// We can have multiple views in the tray for each child. This finds the
|
||||
// We can have multiple views in the tabs panel for each child. This finds the
|
||||
// first view corresponding to the given tab. This varies by Android
|
||||
// version. The first view should always be our ImageButton, but let's
|
||||
// be safe.
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/* 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 { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
var WebcompatReporter = {
|
||||
menuItem: null,
|
||||
menuItemEnabled: null,
|
||||
init: function() {
|
||||
Services.obs.addObserver(this, "DesktopMode:Change", false);
|
||||
Services.obs.addObserver(this, "content-page-shown", false);
|
||||
this.addMenuItem();
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
Services.obs.removeObserver(this, "DesktopMode:Change");
|
||||
|
||||
if (this.menuItem) {
|
||||
NativeWindow.menu.remove(this.menuItem);
|
||||
this.menuItem = null;
|
||||
}
|
||||
},
|
||||
|
||||
observe: function(subject, topic, data) {
|
||||
if (topic === "content-page-shown") {
|
||||
let currentURI = subject.documentURI;
|
||||
if (!this.menuItemEnabled && this.isReportableUrl(currentURI)) {
|
||||
NativeWindow.menu.update(this.menuItem, {enabled: true});
|
||||
this.menuItemEnabled = true;
|
||||
} else if (this.menuItemEnabled && !this.isReportableUrl(currentURI)) {
|
||||
NativeWindow.menu.update(this.menuItem, {enabled: false});
|
||||
this.menuItemEnabled = false;
|
||||
}
|
||||
} else if (topic === "DesktopMode:Change") {
|
||||
let args = JSON.parse(data);
|
||||
let tab = BrowserApp.getTabForId(args.tabId);
|
||||
if (args.desktopMode && tab !== null) {
|
||||
this.reportDesktopModePrompt();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
addMenuItem: function() {
|
||||
this.menuItem = NativeWindow.menu.add({
|
||||
name: this.strings.GetStringFromName("webcompat.menu.name"),
|
||||
callback: () => {
|
||||
let currentURI = BrowserApp.selectedTab.browser.currentURI.spec;
|
||||
this.reportIssue(currentURI);
|
||||
},
|
||||
enabled: false,
|
||||
});
|
||||
},
|
||||
|
||||
isReportableUrl: function(url) {
|
||||
return url !== null && !(url.startsWith("about") ||
|
||||
url.startsWith("chrome") ||
|
||||
url.startsWith("file") ||
|
||||
url.startsWith("resource"));
|
||||
},
|
||||
|
||||
reportDesktopModePrompt: function() {
|
||||
let currentURI = BrowserApp.selectedTab.browser.currentURI.spec;
|
||||
let message = this.strings.GetStringFromName("webcompat.reportDesktopMode.message");
|
||||
let options = {
|
||||
button: {
|
||||
label: this.strings.GetStringFromName("webcompat.reportDesktopModeYes.label"),
|
||||
callback: () => this.reportIssue(currentURI)
|
||||
}
|
||||
};
|
||||
NativeWindow.toast.show(message, "long", options);
|
||||
},
|
||||
|
||||
reportIssue: function(url) {
|
||||
let webcompatURL = new URL("http://webcompat.com/");
|
||||
webcompatURL.searchParams.append("open", "1");
|
||||
webcompatURL.searchParams.append("url", url);
|
||||
BrowserApp.addTab(webcompatURL.href);
|
||||
}
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(WebcompatReporter, "strings", function() {
|
||||
return Services.strings.createBundle("chrome://browser/locale/webcompatReporter.properties");
|
||||
});
|
|
@ -112,6 +112,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "SharedPreferences",
|
|||
["Linkifier", "chrome://browser/content/Linkify.js"],
|
||||
["ZoomHelper", "chrome://browser/content/ZoomHelper.js"],
|
||||
["CastingApps", "chrome://browser/content/CastingApps.js"],
|
||||
#ifdef NIGHTLY_BUILD
|
||||
["WebcompatReporter", "chrome://browser/content/WebcompatReporter.js"],
|
||||
#endif
|
||||
].forEach(function (aScript) {
|
||||
let [name, script] = aScript;
|
||||
XPCOMUtils.defineLazyGetter(window, name, function() {
|
||||
|
@ -334,6 +337,9 @@ var BrowserApp = {
|
|||
// Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
|
||||
SafeBrowsing.init();
|
||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
#endif
|
||||
#ifdef NIGHTLY_BUILD
|
||||
WebcompatReporter.init();
|
||||
#endif
|
||||
} catch(ex) { console.log(ex); }
|
||||
}, false);
|
||||
|
@ -858,6 +864,9 @@ var BrowserApp = {
|
|||
CastingApps.uninit();
|
||||
Distribution.uninit();
|
||||
Tabs.uninit();
|
||||
#ifdef NIGHTLY_BUILD
|
||||
WebcompatReporter.uninit();
|
||||
#endif
|
||||
},
|
||||
|
||||
// This function returns false during periods where the browser displayed document is
|
||||
|
|
|
@ -62,6 +62,9 @@ chrome.jar:
|
|||
content/aboutDevices.xhtml (content/aboutDevices.xhtml)
|
||||
content/aboutDevices.js (content/aboutDevices.js)
|
||||
#endif
|
||||
#ifdef NIGHTLY_BUILD
|
||||
content/WebcompatReporter.js (content/WebcompatReporter.js)
|
||||
#endif
|
||||
|
||||
% content branding %content/branding/
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# 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/.
|
||||
|
||||
# LOCALIZATION NOTE (webcompat.menu.name): A "site issue" is a bug, display,
|
||||
# or functionality problem with a webpage in the browser.
|
||||
webcompat.menu.name=Report Site Issue
|
||||
|
||||
# LOCALIZATION NOTE (webcompat.reportDesktopMode.message): A " site issue" is a
|
||||
# bug, display, or functionality problem with a webpage in the browser.
|
||||
webcompat.reportDesktopMode.message=Report site issue?
|
||||
webcompat.reportDesktopModeYes.label=Report
|
|
@ -39,6 +39,9 @@
|
|||
locale/@AB_CD@/browser/payments.properties (%chrome/payments.properties)
|
||||
locale/@AB_CD@/browser/handling.properties (%chrome/handling.properties)
|
||||
locale/@AB_CD@/browser/webapp.properties (%chrome/webapp.properties)
|
||||
#ifdef NIGHTLY_BUILD
|
||||
locale/@AB_CD@/browser/webcompatReporter.properties (%chrome/webcompatReporter.properties)
|
||||
#endif
|
||||
|
||||
# overrides for toolkit l10n, also for en-US
|
||||
relativesrcdir toolkit/locales:
|
||||
|
|
|
@ -5,171 +5,33 @@
|
|||
"use strict";
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Messaging.jsm");
|
||||
|
||||
const CONFIG = { iceServers: [{ "url": "stun:stun.services.mozilla.com" }] };
|
||||
|
||||
let log = Cu.import("resource://gre/modules/AndroidLog.jsm",
|
||||
{}).AndroidLog.d.bind(null, "TabMirror");
|
||||
|
||||
let failure = function(x) {
|
||||
log("ERROR: " + JSON.stringify(x));
|
||||
};
|
||||
|
||||
let TabMirror = function(deviceId, window) {
|
||||
let out_queue = [];
|
||||
let in_queue = [];
|
||||
let DEBUG = false;
|
||||
|
||||
let log = Cu.import("resource://gre/modules/AndroidLog.jsm",
|
||||
{}).AndroidLog.d.bind(null, "TabMirror");
|
||||
this.deviceId = deviceId;
|
||||
// Save mozRTCSessionDescription and mozRTCIceCandidate for later when the window object is not available.
|
||||
this.RTCSessionDescription = window.mozRTCSessionDescription;
|
||||
this.RTCIceCandidate = window.mozRTCIceCandidate;
|
||||
|
||||
let RTCPeerConnection = window.mozRTCPeerConnection;
|
||||
let RTCSessionDescription = window.mozRTCSessionDescription;
|
||||
let RTCIceCandidate = window.mozRTCIceCandidate;
|
||||
let getUserMedia = window.navigator.mozGetUserMedia.bind(window.navigator);
|
||||
Services.obs.addObserver((aSubject, aTopic, aData) => this._processMessage(aData), "MediaPlayer:Response", false);
|
||||
|
||||
Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
|
||||
in_queue.push(aData);
|
||||
}, "MediaPlayer:Response", false);
|
||||
this._pc = new window.mozRTCPeerConnection(CONFIG, {});
|
||||
|
||||
let poll_timeout = 1000; // ms
|
||||
|
||||
let audio_stream = undefined;
|
||||
let video_stream = undefined;
|
||||
let pc = undefined;
|
||||
|
||||
let poll_success = function(msg) {
|
||||
if (!msg) {
|
||||
poll_error();
|
||||
return;
|
||||
}
|
||||
|
||||
let js;
|
||||
try {
|
||||
js = JSON.parse(msg);
|
||||
} catch(ex) {
|
||||
log("ex: " + ex);
|
||||
}
|
||||
|
||||
let sdp = js.body;
|
||||
|
||||
if (sdp) {
|
||||
if (sdp.sdp) {
|
||||
if (sdp.type === "offer") {
|
||||
process_offer(sdp);
|
||||
} else if (sdp.type === "answer") {
|
||||
process_answer(sdp);
|
||||
}
|
||||
} else {
|
||||
process_ice_candidate(sdp);
|
||||
}
|
||||
}
|
||||
|
||||
window.setTimeout(poll, poll_timeout);
|
||||
};
|
||||
|
||||
let poll_error = function (msg) {
|
||||
window.setTimeout(poll, poll_timeout);
|
||||
};
|
||||
|
||||
let poll = function () {
|
||||
if (in_queue) {
|
||||
poll_success(in_queue.pop());
|
||||
}
|
||||
};
|
||||
|
||||
let failure = function(x) {
|
||||
log("ERROR: " + JSON.stringify(x));
|
||||
};
|
||||
|
||||
|
||||
// Signaling methods
|
||||
let send_sdpdescription= function(sdp) {
|
||||
let msg = {
|
||||
body: sdp
|
||||
};
|
||||
|
||||
sendMessage(JSON.stringify(msg));
|
||||
};
|
||||
|
||||
|
||||
let deobjify = function(x) {
|
||||
return JSON.parse(JSON.stringify(x));
|
||||
};
|
||||
|
||||
|
||||
let process_offer = function(sdp) {
|
||||
pc.setRemoteDescription(new RTCSessionDescription(sdp),
|
||||
set_remote_offer_success, failure);
|
||||
};
|
||||
|
||||
let process_answer = function(sdp) {
|
||||
pc.setRemoteDescription(new RTCSessionDescription(sdp),
|
||||
set_remote_answer_success, failure);
|
||||
};
|
||||
|
||||
let process_ice_candidate = function(msg) {
|
||||
pc.addIceCandidate(new RTCIceCandidate(msg));
|
||||
};
|
||||
|
||||
let set_remote_offer_success = function() {
|
||||
pc.createAnswer(create_answer_success, failure);
|
||||
};
|
||||
|
||||
let set_remote_answer_success= function() {
|
||||
};
|
||||
|
||||
let set_local_success_offer = function(sdp) {
|
||||
send_sdpdescription(sdp);
|
||||
};
|
||||
|
||||
let set_local_success_answer = function(sdp) {
|
||||
send_sdpdescription(sdp);
|
||||
};
|
||||
|
||||
let filter_nonrelay_candidates = function(sdp) {
|
||||
let lines = sdp.sdp.split("\r\n");
|
||||
let lines2 = lines.filter(function(x) {
|
||||
if (!/candidate/.exec(x))
|
||||
return true;
|
||||
if (/relay/.exec(x))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
sdp.sdp = lines2.join("\r\n");
|
||||
};
|
||||
|
||||
let create_offer_success = function(sdp) {
|
||||
pc.setLocalDescription(sdp,
|
||||
function() {
|
||||
set_local_success_offer(sdp);
|
||||
},
|
||||
failure);
|
||||
};
|
||||
|
||||
let create_answer_success = function(sdp) {
|
||||
pc.setLocalDescription(sdp,
|
||||
function() {
|
||||
set_local_success_answer(sdp);
|
||||
},
|
||||
failure);
|
||||
};
|
||||
|
||||
let on_ice_candidate = function (candidate) {
|
||||
send_sdpdescription(candidate.candidate);
|
||||
};
|
||||
|
||||
let ready = function() {
|
||||
pc.createOffer(create_offer_success, failure);
|
||||
poll();
|
||||
};
|
||||
|
||||
|
||||
let config = {
|
||||
iceServers: [{ "url": "stun:stun.services.mozilla.com" }]
|
||||
};
|
||||
|
||||
pc = new RTCPeerConnection(config, {});
|
||||
|
||||
if (!pc) {
|
||||
log("Failure creating Webrtc object");
|
||||
return;
|
||||
if (!this._pc) {
|
||||
throw "Failure creating Webrtc object";
|
||||
}
|
||||
|
||||
pc.onicecandidate = on_ice_candidate;
|
||||
this._pc.onicecandidate = this._onIceCandidate.bind(this);
|
||||
|
||||
let windowId = window.BrowserApp.selectedBrowser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
|
||||
let viewport = window.BrowserApp.selectedTab.getViewport();
|
||||
|
@ -203,41 +65,95 @@ let TabMirror = function(deviceId, window) {
|
|||
}
|
||||
};
|
||||
|
||||
let gUM_success = function(stream){
|
||||
pc.addStream(stream);
|
||||
ready();
|
||||
};
|
||||
window.navigator.mozGetUserMedia(constraints, this._onGumSuccess.bind(this), this._onGumFailure.bind(this));
|
||||
};
|
||||
|
||||
let gUM_failure = function() {
|
||||
log("Could not get video stream");
|
||||
};
|
||||
TabMirror.prototype = {
|
||||
|
||||
getUserMedia( constraints, gUM_success, gUM_failure);
|
||||
_processMessage: function(data) {
|
||||
|
||||
function sendMessage(msg) {
|
||||
let obj = {
|
||||
type: "MediaPlayer:Message",
|
||||
id: deviceId,
|
||||
data: msg
|
||||
};
|
||||
|
||||
if (deviceId) {
|
||||
Services.androidBridge.handleGeckoMessage(obj);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
stop: function() {
|
||||
let msg = JSON.parse(data);
|
||||
|
||||
if (!msg) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.sdp) {
|
||||
if (msg.type === "answer") {
|
||||
this._processAnswer(msg);
|
||||
} else {
|
||||
log("Unandled sdp message type: " + msg.type);
|
||||
}
|
||||
} else {
|
||||
this._processIceCandidate(msg);
|
||||
}
|
||||
},
|
||||
|
||||
// Signaling methods
|
||||
_processAnswer: function(msg) {
|
||||
this._pc.setRemoteDescription(new this.RTCSessionDescription(msg),
|
||||
this._setRemoteAnswerSuccess.bind(this), failure);
|
||||
},
|
||||
|
||||
_processIceCandidate: function(msg) {
|
||||
// WebRTC generates a warning if the success and fail callbacks are not passed in.
|
||||
this._pc.addIceCandidate(new this.RTCIceCandidate(msg), () => log("Ice Candiated added successfuly"), () => log("Failed to add Ice Candidate"));
|
||||
},
|
||||
|
||||
_setRemoteAnswerSuccess: function() {
|
||||
},
|
||||
|
||||
_setLocalSuccessOffer: function(sdp) {
|
||||
this._sendMessage(sdp);
|
||||
},
|
||||
|
||||
_createOfferSuccess: function(sdp) {
|
||||
this._pc.setLocalDescription(sdp, () => this._setLocalSuccessOffer(sdp), failure);
|
||||
},
|
||||
|
||||
_onIceCandidate: function (msg) {
|
||||
log("NEW Ice Candidate: " + JSON.stringify(msg.candidate));
|
||||
this._sendMessage(msg.candidate);
|
||||
},
|
||||
|
||||
_ready: function() {
|
||||
this._pc.createOffer(this._createOfferSuccess.bind(this), failure);
|
||||
},
|
||||
|
||||
_onGumSuccess: function(stream){
|
||||
this._pc.addStream(stream);
|
||||
this._ready();
|
||||
},
|
||||
|
||||
_onGumFailure: function() {
|
||||
log("Could not get video stream");
|
||||
this._pc.close();
|
||||
},
|
||||
|
||||
_sendMessage: function(msg) {
|
||||
if (this.deviceId) {
|
||||
let obj = {
|
||||
type: "MediaPlayer:Message",
|
||||
id: this.deviceId,
|
||||
data: JSON.stringify(msg)
|
||||
};
|
||||
Messaging.sendRequest(obj);
|
||||
}
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
if (this.deviceId) {
|
||||
let obj = {
|
||||
type: "MediaPlayer:End",
|
||||
id: deviceId
|
||||
id: this.deviceId
|
||||
};
|
||||
|
||||
if (deviceId) {
|
||||
Services.androidBridge.handleGeckoMessage(obj);
|
||||
}
|
||||
Services.androidBridge.handleGeckoMessage(obj);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче