Merge m-c to b2ginbound a=merge

This commit is contained in:
Wes Kocher 2015-05-05 20:29:54 -07:00
commit 612682780f
113 changed files with 1453 additions and 1426 deletions

View File

@ -1919,7 +1919,8 @@ pref("dom.serviceWorkers.enabled", true);
#endif
pref("browser.pocket.enabled", false);
pref("browser.pocket.hostname", "localhost");
pref("browser.pocket.api", "api.getpocket.com");
pref("browser.pocket.site", "getpocket.com");
pref("browser.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
pref("browser.pocket.useLocaleList", true);
pref("browser.pocket.enabledLocales", "en-US");

View File

@ -6,17 +6,4 @@
# of Firefox include these strings in browser.properties
pocket-button.label = Pocket
pocket-button.tooltiptext = Send this page to Pocket
pocket-header = Pocket
pocket-login-required-tagline = Catch the best content you find online with Pocket.
pocket-signup-with-fxa = Sign up with Firefox
pocket-signup-with-email = Sign up with email
pocket-account-question = Already have an account?
pocket-login-now = Log in now
pocket-page-saved-header = Page Saved
pocket-open-pocket = Open Pocket
pocket-remove-page = Remove Page
pocket-page-tags-field = Add Tags
pocket-page-tags-add = Save
pocket-page-suggested-tags-header = Suggested Tags
pocket-signup-or = Or
pocket-button.tooltiptext = Save to Pocket

View File

@ -52,7 +52,8 @@ XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
"nsIDNSService");
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
"resource://gre/modules/LightweightThemeManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Pocket",
"resource:///modules/Pocket.jsm");
const nsIWebNavigation = Ci.nsIWebNavigation;
@ -4162,6 +4163,7 @@ var XULBrowserWindow = {
BookmarkingUI.onLocationChange();
SocialUI.updateState(location);
UITour.onLocationChange(location);
Pocket.onLocationChange(browser, aLocationURI);
}
// Utility functions for disabling find

View File

@ -730,9 +730,9 @@
fullscreentoolbar="true" mode="icons" customizable="true"
iconsize="small"
#ifdef MOZ_DEV_EDITION
defaultset="urlbar-container,search-container,developer-button,bookmarks-menu-button,downloads-button,home-button,loop-button"
defaultset="urlbar-container,search-container,developer-button,bookmarks-menu-button,pocket-button,downloads-button,home-button,loop-button"
#else
defaultset="urlbar-container,search-container,bookmarks-menu-button,downloads-button,home-button,loop-button"
defaultset="urlbar-container,search-container,bookmarks-menu-button,pocket-button,downloads-button,home-button,loop-button"
#endif
customizationtarget="nav-bar-customization-target"
overflowable="true"

View File

@ -187,7 +187,7 @@ nsContextMenu.prototype = {
(uri.schemeIs("http") || uri.schemeIs("https") ||
(uri.schemeIs("about") && ReaderMode.getOriginalUrl(uri.spec)));
}
this.showItem("context-pocket", canPocket && window.Pocket && Pocket.isLoggedIn);
this.showItem("context-pocket", canPocket && window.pktApi && window.pktApi.isUserLoggedIn());
},
initViewItems: function CM_initViewItems() {

View File

@ -208,6 +208,15 @@ let CustomizableUIInternal = {
"loop-button",
];
// Insert the Pocket button after the bookmarks button if it's present.
for (let widgetDefinition of CustomizableWidgets) {
if (widgetDefinition.id == "pocket-button") {
let idx = navbarPlacements.indexOf("bookmarks-menu-button") + 1;
navbarPlacements.splice(idx, 0, widgetDefinition.id);
break;
}
}
if (Services.prefs.getBoolPref(kPrefWebIDEInNavbar)) {
navbarPlacements.push("webide-button");
}
@ -288,19 +297,40 @@ let CustomizableUIInternal = {
},
_introduceNewBuiltinWidgets: function() {
if (!gSavedState || gSavedState.currentVersion >= kVersion) {
// We should still enter even if gSavedState.currentVersion >= kVersion
// because the per-widget pref facility is independent of versioning.
if (!gSavedState) {
return;
}
let currentVersion = gSavedState.currentVersion;
for (let [id, widget] of gPalette) {
if (widget._introducedInVersion > currentVersion &&
widget.defaultArea) {
let futurePlacements = gFuturePlacements.get(widget.defaultArea);
if (futurePlacements) {
futurePlacements.add(id);
} else {
gFuturePlacements.set(widget.defaultArea, new Set([id]));
if (widget.defaultArea) {
let shouldAdd = false;
let shouldSetPref = false;
let prefId = "browser.toolbarbuttons.introduced." + widget.id;
if (widget._introducedInVersion === "pref") {
try {
shouldAdd = !Services.prefs.getBoolPref(prefId);
} catch (ex) {
// Pref doesn't exist:
shouldAdd = true;
}
shouldSetPref = shouldAdd;
} else if (widget._introducedInVersion > currentVersion) {
shouldAdd = true;
}
if (shouldAdd) {
let futurePlacements = gFuturePlacements.get(widget.defaultArea);
if (futurePlacements) {
futurePlacements.add(id);
} else {
gFuturePlacements.set(widget.defaultArea, new Set([id]));
}
if (shouldSetPref) {
Services.prefs.setBoolPref(prefId, true);
}
}
}
}
@ -2087,6 +2117,14 @@ let CustomizableUIInternal = {
// opened - so we know there's no build areas to handle. Also, builtin
// widgets are expected to be (mostly) static, so shouldn't affect the
// current placement settings.
// This allows a widget to be both built-in by default but also able to be
// destroyed based on criteria that may not be available when the widget is
// created -- for example, because some other feature in the browser
// supersedes the widget.
let conditionalDestroyPromise = aData.conditionalDestroyPromise || null;
delete aData.conditionalDestroyPromise;
let widget = this.normalizeWidget(aData, CustomizableUI.SOURCE_BUILTIN);
if (!widget) {
ERROR("Error creating builtin widget: " + aData.id);
@ -2095,6 +2133,16 @@ let CustomizableUIInternal = {
LOG("Creating built-in widget with id: " + widget.id);
gPalette.set(widget.id, widget);
if (conditionalDestroyPromise) {
conditionalDestroyPromise.then(shouldDestroy => {
if (shouldDestroy) {
this.destroyWidget(widget.id);
}
}, err => {
Cu.reportError(err);
});
}
},
// Returns true if the area will eventually lazily restore (but hasn't yet).

View File

@ -26,6 +26,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
"resource://gre/modules/CharsetMenu.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SocialService",
"resource://gre/modules/SocialService.jsm");
XPCOMUtils.defineLazyGetter(this, "CharsetBundle", function() {
const kCharsetBundle = "chrome://global/locale/charsetMenu.properties";
@ -1081,16 +1085,44 @@ if (Services.prefs.getBoolPref("browser.pocket.enabled")) {
if (isEnabledForLocale) {
let pocketButton = {
id: "pocket-button",
defaultArea: CustomizableUI.AREA_NAVBAR,
introducedInVersion: "pref",
type: "view",
viewId: "PanelUI-pocketView",
label: PocketBundle.GetStringFromName("pocket-button.label"),
tooltiptext: PocketBundle.GetStringFromName("pocket-button.tooltiptext"),
onViewShowing: Pocket.onPanelViewShowing,
onViewHiding: Pocket.onPanelViewHiding,
// If the user has the "classic" Pocket add-on installed, use that instead
// and destroy the widget.
conditionalDestroyPromise: new Promise(resolve => {
AddonManager.getAddonByID("isreaditlater@ideashower.com", addon => {
resolve(addon && addon.isActive);
});
}),
};
CustomizableWidgets.push(pocketButton);
CustomizableUI.addListener(pocketButton);
// Uninstall the Pocket social provider if it exists, but only if we haven't
// already uninstalled it in this manner. That way the user can reinstall
// it if they prefer it without its being uninstalled every time they start
// the browser.
let origin = "https://getpocket.com";
SocialService.getProvider(origin, provider => {
if (provider) {
let pref = "social.backup.getpocket-com";
if (!Services.prefs.prefHasUserValue(pref)) {
let str = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
str.data = JSON.stringify(provider.manifest);
Services.prefs.setComplexValue(pref, Ci.nsISupportsString, str);
SocialService.uninstallProvider(origin, () => {});
}
}
});
}
}

View File

@ -71,6 +71,27 @@ function test() {
ok(haveNavbarPlacements, "Should have placements for nav-bar");
if (haveNavbarPlacements) {
let placements = [...gFuturePlacements.get(CustomizableUI.AREA_NAVBAR)];
// Ignore widgets that are placed using the pref facility and not the
// versioned facility. They're independent of kVersion and the saved
// state's current version, so they may be present in the placements.
for (let i = 0; i < placements.length; ) {
if (placements[i] == testWidgetNew.id) {
i++;
continue;
}
let pref = "browser.toolbarbuttons.introduced." + placements[i];
let introduced = false;
try {
introduced = Services.prefs.getBoolPref(pref);
} catch (ex) {}
if (!introduced) {
i++;
continue;
}
placements.splice(i, 1);
}
is(placements.length, 1, "Should have 1 newly placed widget in nav-bar");
is(placements[0], testWidgetNew.id, "Should have our test widget to be placed in nav-bar");
}

View File

@ -734,6 +734,7 @@ BrowserGlue.prototype = {
LightweightThemeManager.addBuiltInTheme({
id: "firefox-devedition@mozilla.org",
name: themeName,
accentcolor: "transparent",
headerURL: "resource:///chrome/browser/content/browser/defaultthemes/devedition.header.png",
iconURL: "resource:///chrome/browser/content/browser/defaultthemes/devedition.icon.png",
author: vendorShortName,

View File

@ -7,118 +7,14 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
this.EXPORTED_SYMBOLS = ["Pocket"];
Cu.import("resource://gre/modules/Http.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "PocketBundle", function() {
const kPocketBundle = "chrome://browser/content/browser-pocket.properties";
return Services.strings.createBundle(kPocketBundle);
});
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode",
"resource://gre/modules/ReaderMode.jsm");
let Pocket = {
get isLoggedIn() {
return !!this._accessToken;
},
prefBranch: Services.prefs.getBranch("browser.pocket.settings."),
get hostname() Services.prefs.getCharPref("browser.pocket.hostname"),
get listURL() { return "https://" + Pocket.hostname; },
get _accessToken() {
let sessionId, accessToken;
let cookies = Services.cookies.getCookiesFromHost(this.hostname);
while (cookies.hasMoreElements()) {
let cookie = cookies.getNext().QueryInterface(Ci.nsICookie2);
if (cookie.name == "ftv1")
accessToken = cookie.value;
else if (cookie.name == "fsv1")
sessionId = cookie.value;
}
if (!accessToken)
return null;
let lastSessionId;
try {
lastSessionId = this.prefBranch.getCharPref("sessionId");
} catch (e) { }
if (sessionId != lastSessionId)
this.prefBranch.deleteBranch("");
this.prefBranch.setCharPref("sessionId", sessionId);
return accessToken;
},
save(url, title) {
let since = "0";
try {
since = this.prefBranch.getCharPref("latestSince");
} catch (e) { }
let data = {url: url, since: since, title: title};
return new Promise((resolve, reject) => {
this._send("firefox/save", data,
data => {
this.prefBranch.setCharPref("latestSince", data.since);
resolve(data.item);
},
error => { reject(error); }
);
});
},
remove(itemId) {
let actions = [{ action: "delete", item_id: itemId }];
this._send("send", {actions: JSON.stringify(actions)});
},
tag(itemId, tags) {
let actions = [{ action: "tags_add", item_id: itemId, tags: tags }];
this._send("send", {actions: JSON.stringify(actions)});
},
_send(url, data, onSuccess, onError) {
let token = this._accessToken;
if (!token)
throw "Attempted to send a request to Pocket while not logged in";
let browserLocale = Cc["@mozilla.org/chrome/chrome-registry;1"]
.getService(Ci.nsIXULChromeRegistry)
.getSelectedLocale("browser");
let postData = [
["access_token", token],
["consumer_key", "40249-e88c401e1b1f2242d9e441c4"],
["locale_lang", browserLocale]
];
for (let key in data)
postData.push([key, data[key]]);
httpRequest("https://" + this.hostname + "/v3/" + url, {
headers: [["X-Accept", "application/json"]],
postData: postData,
onLoad: (responseText) => {
if (onSuccess)
onSuccess(JSON.parse(responseText));
},
onError: function(error, responseText, xhr) {
if (!onError)
return;
let errorMessage = xhr.getResponseHeader("X-Error");
onError(new Error(error + " - " + errorMessage));
}
});
},
/**
* Functions related to the Pocket panel UI.
*/
@ -132,4 +28,28 @@ let Pocket = {
let window = event.target.ownerDocument.defaultView;
window.pktUI.pocketPanelDidHide(event);
},
// Called on tab/urlbar/location changes and after customization. Update
// anything that is tab specific.
onLocationChange(browser, locationURI) {
if (!locationURI) {
return;
}
let widget = CustomizableUI.getWidget("pocket-button");
for (let instance of widget.instances) {
let node = instance.node;
if (!node ||
node.ownerDocument != browser.ownerDocument) {
continue;
}
if (node) {
let win = browser.ownerDocument.defaultView;
node.disabled = win.pktApi.isUserLoggedIn() &&
!locationURI.schemeIs("http") &&
!locationURI.schemeIs("https") &&
!(locationURI.schemeIs("about") &&
locationURI.spec.toLowerCase().startsWith("about:reader?url="));
}
}
},
};

View File

@ -18,14 +18,20 @@ browser.jar:
content/browser/pocket/panels/img/pocketlogosolo@2x.png (panels/img/pocketlogosolo@2x.png)
content/browser/pocket/panels/img/pocketmenuitem16.png (panels/img/pocketmenuitem16.png)
content/browser/pocket/panels/img/pocketmenuitem16@2x.png (panels/img/pocketmenuitem16@2x.png)
content/browser/pocket/panels/img/pocketmultidevices@1x.png (panels/img/pocketmultidevices@1x.png)
content/browser/pocket/panels/img/pocketmultidevices@2x.png (panels/img/pocketmultidevices@2x.png)
content/browser/pocket/panels/img/pocketsignup_button@1x.png (panels/img/pocketsignup_button@1x.png)
content/browser/pocket/panels/img/pocketsignup_button@2x.png (panels/img/pocketsignup_button@2x.png)
content/browser/pocket/panels/img/pocketsignup_devices@1x.png (panels/img/pocketsignup_devices@1x.png)
content/browser/pocket/panels/img/pocketsignup_devices@2x.png (panels/img/pocketsignup_devices@2x.png)
content/browser/pocket/panels/img/pocketsignup_hero@1x.png (panels/img/pocketsignup_hero@1x.png)
content/browser/pocket/panels/img/pocketsignup_hero@2x.png (panels/img/pocketsignup_hero@2x.png)
content/browser/pocket/panels/img/signup_firefoxlogo@1x.png (panels/img/signup_firefoxlogo@1x.png)
content/browser/pocket/panels/img/signup_firefoxlogo@2x.png (panels/img/signup_firefoxlogo@2x.png)
content/browser/pocket/panels/img/signup_help@1x.png (panels/img/signup_help@1x.png)
content/browser/pocket/panels/img/signup_help@2x.png (panels/img/signup_help@2x.png)
content/browser/pocket/panels/img/tag_close@1x.png (panels/img/tag_close@1x.png)
content/browser/pocket/panels/img/tag_close@2x.png (panels/img/tag_close@2x.png)
content/browser/pocket/panels/img/tag_closeactive@1x.png (panels/img/tag_closeactive@1x.png)
content/browser/pocket/panels/img/tag_closeactive@2x.png (panels/img/tag_closeactive@2x.png)
content/browser/pocket/panels/js/dictionary.js (panels/js/dictionary.js)
content/browser/pocket/panels/js/messages.js (panels/js/messages.js)
content/browser/pocket/panels/js/saved.js (panels/js/saved.js)
@ -37,3 +43,4 @@ browser.jar:
content/browser/pocket/panels/tmpl/saved_premiumshell.handlebars (panels/tmpl/saved_premiumshell.handlebars)
content/browser/pocket/panels/tmpl/saved_shell.handlebars (panels/tmpl/saved_shell.handlebars)
content/browser/pocket/panels/tmpl/signup_shell.handlebars (panels/tmpl/signup_shell.handlebars)
content/browser/pocket/panels/tmpl/signupstoryboard_shell.handlebars (panels/tmpl/signupstoryboard_shell.handlebars)

View File

@ -42,6 +42,9 @@
// TODO : [nice to have] - Immediately save, buffer the actions in a local queue and send (so it works offline, works like our native extensions)
// TODO : Remove console.log entries
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm");
var pktUI = (function() {
// -- Initialization (on startup and new windows) -- //
@ -277,16 +280,35 @@ var pktUI = (function() {
* Show the sign-up panel
*/
function showSignUp() {
showPanel("chrome://browser/content/pocket/panels/signup.html", {
onShow: function() {
// Open and resize the panel
resizePanel({
getFirefoxAccountSignedInUser(function(userdata)
{
var fxasignedin = (typeof userdata == 'object' && userdata !== null) ? '1' : '0';
var startheight = 490;
if (pktApi.getSignupAB() == 'storyboard')
{
startheight = 460;
if (fxasignedin == '1')
{
startheight = 406;
}
}
else
{
if (fxasignedin == '1')
{
startheight = 436;
}
}
showPanel("chrome://browser/content/pocket/panels/signup.html?pockethost=" + Services.prefs.getCharPref("browser.pocket.site") + "&fxasignedin=" + fxasignedin + "&variant=" + pktApi.getSignupAB(), {
onShow: function() {
resizePanel({
width: 300,
height: 550
});
},
onHide: panelDidHide,
});
height: startheight
});
},
onHide: panelDidHide,
});
});
}
/**
@ -294,22 +316,31 @@ var pktUI = (function() {
*/
function saveAndShowConfirmation(url, title) {
// Validate parameter
// TODO: Show some kind of error
if (typeof url === 'undefined') { return; }
if (!url.startsWith("http") && !url.startsWith('https')) { return; };
// Validate input parameter
if (typeof url !== 'undefined' && url.startsWith("about:reader?url=")) {
url = ReaderMode.getOriginalUrl(url);
}
showPanel("chrome://browser/content/pocket/panels/saved.html?premiumStatus=" + (pktApi.isPremiumUser() ? '1' : '0'), {
var isValidURL = (typeof url !== 'undefined' && (url.startsWith("http") || url.startsWith('https')));
showPanel("chrome://browser/content/pocket/panels/saved.html?pockethost=" + Services.prefs.getCharPref("browser.pocket.site") + "&premiumStatus=" + (pktApi.isPremiumUser() ? '1' : '0'), {
onShow: function() {
// Open and resize the panel
resizePanel({
width: 350,
height: 266
height: 263
});
var options = {
success: function(data, response) {
// Send error message for invalid url
if (!isValidURL) {
var error = new Error('Only links can be saved');
sendErrorMessage('saveLink', error);
return;
}
// Add url
var options = {
success: function(data, request) {
var item = data.item;
var successResponse = {
status: "success",
@ -317,7 +348,14 @@ var pktUI = (function() {
};
sendMessage('saveLink', successResponse);
},
error: function(error, response) {
error: function(error, request) {
// If user is not authorized show singup page
if (request.status === 401) {
showSignUp();
return;
}
// Send error message to panel
sendErrorMessage('saveLink', error);
}
}
@ -388,7 +426,6 @@ var pktUI = (function() {
* Called when the signup and saved panel was hidden
*/
function panelDidHide() {
console.log("Panel did hide");
}
// -- Communication to Panels -- //
@ -478,7 +515,7 @@ var pktUI = (function() {
activate = payload.activate;
}
openTabWithUrl(payload.url, activate);
sendMessage("openTabWithUrlResponse", url);
sendMessage("openTabWithUrlResponse", payload.url);
});
// Close the panel
@ -493,7 +530,6 @@ var pktUI = (function() {
// Callback post initialization to tell background script that panel is "ready" for communication.
addMessageListener("listenerReady", function(payload) {
console.log('got a listener init');
});
addMessageListener("resizePanel", function(payload) {
@ -604,13 +640,12 @@ var pktUI = (function() {
return _isHidden;
}
function isUserLoggedIntoFxA() {
// TODO : verify with Firefox this is the right way to do this
var user = fxAccounts.getSignedInUser();
if (user && user.email)
return true;
return false;
function getFirefoxAccountSignedInUser(callback) {
fxAccounts.getSignedInUser().then(userData => {
callback(userData);
}).then(null, error => {
callback();
});
}
/**
@ -705,7 +740,6 @@ var pktUI = (function() {
*/
return {
onLoad: onLoad,
onUnload: onUnload,
pocketButtonOnCommand: pocketButtonOnCommand,
pocketPanelDidShow: pocketPanelDidShow,

View File

@ -8,8 +8,6 @@
html {
font-family: sans-serif; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
@ -291,7 +289,6 @@ button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
@ -337,17 +334,6 @@ input[type="radio"] {
padding: 0; /* 2 */
}
/**
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
* `font-size` values of the `input`, it causes the cursor style of the
* decrement button to change from `default` to `text`.
*/
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
@ -355,23 +341,9 @@ input[type="number"]::-webkit-outer-spin-button {
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
* Safari (but not Chrome) clips the cancel button when the search input has
* padding (and `textfield` appearance).
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Define consistent border, margin, and padding.
*/
@ -424,4 +396,4 @@ table {
td,
th {
padding: 0;
}
}

View File

@ -23,7 +23,6 @@
font-family: "FiraSans", "Helvetica Neue", Helvetica, Arial, sans-serif;
padding: 0;
position: relative;
text-shadow: 0;
text-align: center;
}
.pkt_ext_cf:after {
@ -42,17 +41,8 @@
/*=Loading spinner
--------------------------------------------------------------------------------------- */
@-moz-keyframes pkt_ext_spin {
@keyframes pkt_ext_spin {
to {
-moz-transform: rotate(1turn);
-webkit-transform: rotate(1turn);
transform: rotate(1turn);
}
}
@-webkit-keyframes pkt_ext_spin {
to {
-moz-transform: rotate(1turn);
-webkit-transform: rotate(1turn);
transform: rotate(1turn);
}
}
@ -68,8 +58,6 @@
font-size: 10px;
text-indent: 999em;
overflow: hidden;
-moz-animation: pkt_ext_spin 0.7s infinite steps(8);
-webkit-animation: pkt_ext_spin 0.7s infinite steps(8);
animation: pkt_ext_spin 0.7s infinite steps(8);
}
.pkt_ext_containersaved .pkt_ext_loadingspinner:before,
@ -85,28 +73,20 @@
border-radius: .2em;
background: #eee;
box-shadow: 0 1.75em #eee;
-webkit-transform-origin: 50% 1.25em;
-moz-transform-origin: 50% 1.25em;
transform-origin: 50% 1.25em;
}
.pkt_ext_containersaved .pkt_ext_loadingspinner:before {
background: #555;
}
.pkt_ext_containersaved .pkt_ext_loadingspinner:after {
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
transform: rotate(-45deg);
background: #777;
}
.pkt_ext_containersaved .pkt_ext_loadingspinner > div:before {
-webkit-transform: rotate(-90deg);
-moz-transform: rotate(-90deg);
transform: rotate(-90deg);
background: #999;
}
.pkt_ext_containersaved .pkt_ext_loadingspinner > div:after {
-webkit-transform: rotate(-135deg);
-moz-transform: rotate(-135deg);
transform: rotate(-135deg);
background: #bbb;
}
@ -125,42 +105,32 @@
position: relative;
z-index: 10;
}
.pkt_ext_container_detailactive .pkt_ext_initload,
.pkt_ext_container_finalerrorstate .pkt_ext_initload {
-webkit-transition: opacity 0.2s ease-out;
-o-transition: opacity 0.2s ease-out;
.pkt_ext_container_detailactive .pkt_ext_initload {
transition: opacity 0.2s ease-out;
opacity: 0;
}
.pkt_ext_container_detailactive .pkt_ext_initload .pkt_ext_loadingspinner,
.pkt_ext_container_finalerrorstate .pkt_ext_initload .pkt_ext_loadingspinner {
-moz-animation: none;
-webkit-animation: none;
.pkt_ext_container_finalstate .pkt_ext_initload .pkt_ext_loadingspinner {
animation: none;
}
.pkt_ext_container_detailactive .pkt_ext_detail {
-webkit-transition: opacity 0.2s ease-out 0.4s;
-moz-transition: opacity 0.2s ease-out 0.4s;
-ms-transition: opacity 0.2s ease-out 0.4s;
-o-transition: opacity 0.2s ease-out 0.4s;
max-height: 20em;
opacity: 1;
transition: opacity 0.2s ease-out 0.4s;
transition: opacity 0.2s ease-out;
}
.pkt_ext_container_finalstate .pkt_ext_edit_msg,
.pkt_ext_container_finalstate .pkt_ext_tag_detail,
.pkt_ext_container_finalstate .pkt_ext_suggestedtag_detail,
.pkt_ext_container_finalstate .pkt_ext_item_actions {
-webkit-transition: opacity 0.2s ease-out;
-moz-transition: opacity 0.2s ease-out;
-ms-transition: opacity 0.2s ease-out;
-o-transition: opacity 0.2s ease-out;
opacity: 0;
transition: opacity 0.2s ease-out;
}
.pkt_ext_container_finalerrorstate .pkt_ext_edit_msg,
.pkt_ext_container_finalerrorstate .pkt_ext_tag_detail {
.pkt_ext_container_finalerrorstate .pkt_ext_tag_detail,
.pkt_ext_container_finalerrorstate .pkt_ext_suggestedtag_detail,
.pkt_ext_container_finalerrorstate .pkt_ext_item_actions {
display: none;
transition: none;
}
.pkt_ext_containersaved h2 {
background: transparent;
@ -168,17 +138,73 @@
color: #333;
display: block;
float: none;
font-size: 1.125em;
font-size: 22px;
font-size: 18px;
font-weight: normal;
letter-spacing: normal;
line-height: 1;
margin: 18px 0 4px;
margin: 19px 0 4px;
padding: 0;
position: relative;
text-align: left;
text-transform: none;
}
@keyframes fade_in_out {
0% {
opacity: 1;
top: 0;
}
50% {
opacity: 0;
top: 0;
}
51% {
opacity: 0;
top: 10px;
}
100% {
opacity: 1;
top: 10px;
}
}
@keyframes fade_in_outalt {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
51% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.pkt_ext_container_finalstate h2 {
animation: fade_in_out 0.4s ease-out;
top: 10px;
}
.pkt_ext_container_finalerrorstate h2 {
animation: fade_int_outalt 0.4s ease-out;
color: #d74345;
top: 0;
}
.pkt_ext_containersaved .pkt_ext_errordetail {
display: none;
font-size: 12px;
font-weight: normal;
left: 6.4em;
opacity: 0;
position: absolute;
top: 2.7em;
text-align: left;
visibility: hidden;
}
.pkt_ext_container_finalerrorstate .pkt_ext_errordetail {
display: block;
opacity: 1;
visibility: visible;
}
.pkt_ext_containersaved .pkt_ext_logo {
background: url(../img/pocketlogosolo@1x.png) center center no-repeat;
display: block;
@ -188,7 +214,7 @@
position: relative;
width: 44px;
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) {
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersaved .pkt_ext_logo {
background-image: url(../img/pocketlogosolo@2x.png);
background-size: 44px 40px;
@ -197,10 +223,6 @@
.pkt_ext_containersaved .pkt_ext_topdetail {
float: left;
}
.pkt_ext_container_finalerrorstate h2 {
line-height: 1.2em;
margin-bottom: 1.5em;
}
.pkt_ext_containersaved .pkt_ext_edit_msg {
display: none;
font-size: 0.875em;
@ -241,8 +263,6 @@
width: 100%;
}
.pkt_ext_containersaved .pkt_ext_item_actions li {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
background: none;
border: 0;
@ -250,7 +270,7 @@
list-style: none;
line-height: 0.8;
height: auto;
padding-right: 0.5em;
padding-right: 0.4em;
width: auto;
}
.pkt_ext_containersaved .pkt_ext_item_actions li:before {
@ -283,7 +303,7 @@
}
.pkt_ext_containersaved .pkt_ext_item_actions a:hover {
color: #008acb;
text-decoration: none;
text-decoration: underline;
}
.pkt_ext_containersaved .pkt_ext_item_actions a:before,
.pkt_ext_containersaved .pkt_ext_item_actions a:after {
@ -299,7 +319,7 @@
text-align: right;
}
.pkt_ext_containersaved .pkt_ext_item_actions .pkt_ext_removeitem {
padding-left: 0.2em;
padding-left: 0;
}
.pkt_ext_containersaved .pkt_ext_close {
background: url(../img/tag_close@1x.png) center center no-repeat;
@ -315,7 +335,7 @@
top: -1em;
width: 10px;
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) {
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersaved .pkt_ext_close {
background-image: url(../img/tag_close@2x.png);
background-size: 8px 8px;
@ -335,20 +355,10 @@
clear: both;
margin: 1.25em 1em;
padding: 0;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-box;
display: -webkit-flex;
display: flex;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-moz-box-flex: 1;
-ms-flex: 1;
-webkit-box-flex: 1;
-webkit-flex: 1;
flex: 1;
background-color: #fff;
border-right: 1px solid #c3c3c3;
@ -384,7 +394,6 @@
content: none;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper input {
-webkit-appearance: caret;
border: 0;
box-shadow: none;
background-color: #fff;
@ -398,7 +407,7 @@
padding: 3px 2px 1px;
text-transform: none;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper input::-webkit-input-placeholder {
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper input::-moz-placeholder {
color: #a9a9a9;
letter-spacing: normal;
text-transform: none;
@ -408,8 +417,6 @@
opacity: 0.5;
}
.pkt_ext_containersaved .pkt_ext_btn {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
float: none;
font-size: 0.875em;
@ -442,8 +449,6 @@
}
.pkt_ext_container_detailactive .pkt_ext_suggestedtag_detail {
opacity: 1;
-webkit-transition: opacity 0.2s ease-out, visibility 0.2s ease-out;
-o-transition: opacity 0.2s ease-out, visibility 0.2s ease-out;
transition: opacity 0.2s ease-out, visibility 0.2s ease-out;
visibility: visible;
}
@ -523,6 +528,7 @@
}
.pkt_ext_containersaved .token_tag:hover {
background-color: #008acb;
border-color: #008acb;
color: #fff;
text-decoration: none;
}
@ -530,6 +536,15 @@
.pkt_ext_containersaved .token_tag:after {
content: none;
}
.pkt_ext_containersaved .token_tag:hover span {
background-image: url(../img/tag_closeactive@1x.png);
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersaved .token_tag:hover span {
background-image: url(../img/tag_closeactive@2x.png);
background-size: 8px 8px;
}
}
.pkt_ext_containersaved .pkt_ext_recenttag_detail_disabled .token_tag,
.pkt_ext_containersaved .pkt_ext_recenttag_detail_disabled .token_tag:hover,
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail_disabled .token_tag,
@ -567,10 +582,6 @@
/*=Token input/autocomplete
--------------------------------------------------------------------------------------- */
.token-input-dropdown-tag {
-moz-border-radius: 4px;
-moz-box-sizing: border-box;
-webkit-border-radius: 4px;
-webkit-box-sizing: border-box;
border-radius: 4px;
box-sizing: border-box;
background: #fff;
@ -621,11 +632,8 @@
.token-input-list li input {
border: 0;
background-color: white;
-webkit-appearance: caret;
}
.pkt_ext_containersaved .token-input-token {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
background: none;
border-radius: 4px;
border: 1px solid #c3c3c3;
@ -676,19 +684,38 @@
width: 0;
}
.pkt_ext_containersaved .token-input-token span {
color: #777;
background: url(../img/tag_close@1x.png) center center no-repeat;
cursor: pointer;
display: inline-block;
margin: 0 0 0 10px;
vertical-align: top;
height: 8px;
margin: 0 2px 0 8px;
overflow: hidden;
width: 8px;
text-indent: -99px;
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersaved .token-input-token span {
background-image: url(../img/tag_close@2x.png);
background-size: 8px 8px;
}
}
.pkt_ext_containersaved .token-input-selected-token {
background-color: #008acb;
border-color: #008acb;
color: #fff;
}
.pkt_ext_containersaved .token-input-selected-token span {
background-image: url(../img/tag_closeactive@1x.png);
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersaved .token-input-selected-token span {
background-image: url(../img/tag_closeactive@2x.png);
background-size: 8px 8px;
}
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper_disabled .token-input-selected-token {
background-color: #f7f7f7;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper_disabled .token-input-selected-token span {
color: #bbb;
}
}

View File

@ -6,6 +6,7 @@
* Contents:
* Global
* Core detail
* Core detail - storyboard
* Buttons
* Responsive
*/
@ -24,8 +25,6 @@
text-align: center;
}
.pkt_ext_containersignup_inactive {
-moz-animation: pkt_ext_hide 0.3s ease-out;
-webkit-animation: pkt_ext_hide 0.3s ease-out;
animation: pkt_ext_hide 0.3s ease-out;
opacity: 0;
visibility: hidden;
@ -35,20 +34,6 @@
display:table;
clear:both;
}
@-webkit-keyframes pkt_ext_hide {
0% {
opacity: 1;
visibility: visible;
}
99% {
opacity: 0;
visibility: visible;
}
100% {
opacity: 0;
visibility: hidden;
}
}
@keyframes pkt_ext_hide {
0% {
opacity: 1;
@ -72,57 +57,48 @@
font-family: "FiraSans", "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1.3;
margin: 0 auto 1.5em;
max-width: 250px;
max-width: 260px;
}
.pkt_ext_containersignup a {
color: #4c8fd0;
text-decoration: none;
}
.pkt_ext_containersignup a:hover {
color: #3076b9;
}
.pkt_ext_containersignup .pkt_ext_introrecommend {
background-color: #f4f4f4;
color: #333;
font-size: 12px;
font-weight: normal;
margin: 0;
padding: 1em 0.5em;
}
.pkt_ext_containersignup .pkt_ext_introdetail {
background-color: #fbfbfb;
border: 1px solid #c1c1c1;
border-width: 1px 0;
border-width: 0 0 1px;
}
.pkt_ext_containersignup .pkt_ext_logo {
background: url(../img/pocketlogo@1x.png) center bottom no-repeat;
display: block;
height: 38px;
height: 32px;
margin: 0 auto 15px;
padding-top: 25px;
position: relative;
text-indent: -9999px;
width: 147px;
width: 123px;
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) {
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersignup .pkt_ext_logo {
background-image: url(../img/pocketlogo@2x.png);
background-size: 147px 38px;
background-size: 123px 32px;
}
}
.pkt_ext_containersignup .pkt_ext_introimg {
background: url(../img/pocketmultidevices@1x.png) center center no-repeat;
background: url(../img/pocketsignup_hero@1x.png) center center no-repeat;
display: block;
height: 122px;
margin: 10px auto 20px;
height: 125px;
margin: 0 auto;
position: relative;
text-indent: -9999px;
width: 171px;
width: 255px;
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) {
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersignup .pkt_ext_introimg {
background-image: url(../img/pocketmultidevices@2x.png);
background-size: 171px 122px;
background-image: url(../img/pocketsignup_hero@2x.png);
background-size: 255px 125px;
}
}
.pkt_ext_containersignup .pkt_ext_tagline {
@ -162,6 +138,72 @@
margin-top: 15px;
}
/*=Core detail - storyboard
--------------------------------------------------------------------------------------- */
.pkt_ext_introstory {
align-items: center;
display: flex;
padding: 20px;
}
.pkt_ext_introstory:after {
clear: both;
content: "";
display: table;
}
.pkt_ext_introstory p {
margin-bottom: 0;
text-align: left;
}
.pkt_ext_introstoryone {
padding: 20px 18px 15px 20px;
}
.pkt_ext_introstorytwo {
padding: 3px 0 0 20px;
}
.pkt_ext_introstorytwo .pkt_ext_tagline {
margin-bottom: 1.5em;
}
.pkt_ext_introstory_text {
flex: 1;
}
.pkt_ext_introstoryone_img,
.pkt_ext_introstorytwo_img {
display: block;
overflow: hidden;
position: relative;
text-indent: -999px;
}
.pkt_ext_introstoryone_img {
background: url(../img/pocketsignup_button@1x.png) center right no-repeat;
height: 82px;
padding: 0 0 0 0.7em;
width: 82px;
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_introstoryone_img {
background-image: url(../img/pocketsignup_button@2x.png);
background-size: 82px 82px;
}
}
.pkt_ext_introstorytwo_img {
background: url(../img/pocketsignup_devices@1x.png) bottom right no-repeat;
height: 110px;
padding: 1em 0 0 0.7em;
width: 124px;
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_introstorytwo_img {
background-image: url(../img/pocketsignup_devices@2x.png);
background-size: 124px 110px;
}
}
.pkt_ext_introstorydivider {
border-top: 1px solid #c1c1c1;
height: 1px;
margin: 0 auto;
width: 125px;
}
/*=Buttons
--------------------------------------------------------------------------------------- */
.pkt_ext_containersignup .btn {
@ -218,8 +260,6 @@
.pkt_ext_containersignup .signupinterim-btn-signup,
.pkt_ext_containersignup .forgot-btn-submit,
.pkt_ext_containersignup .forgotreset-btn-change {
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box;
min-width: 12.125em;
padding: 0.8em 1.1875em;
box-sizing: content-box;
@ -242,7 +282,7 @@
width: 22px;
position: absolute;
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 144dpi) {
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersignup .signup-btn-firefox .logo {
background-image: url(../img/signup_firefoxlogo@2x.png);
background-size: 22px 22px;
@ -264,4 +304,4 @@
.pkt_ext_containersignup .btn-disabled .text {
color: #ccc;
color: rgba(255,255,255,0.6);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

View File

@ -2,21 +2,26 @@ Translations = {};
Translations.en =
{
addtags: "Add Tags",
alreadyhaveacct: "Already have an account?",
alreadyhaveacct: "Already a Pocket user?",
learnmore: "Learn More",
loginnow: "Log in",
onlylinkssaved: "Only links can be saved",
or: "or",
pagenotsaved: "Page Not Saved",
pageremoved: "Page Removed",
pagesaved: "Saved to Pocket",
processingremove: "Removing Page...",
processingtags: "Adding tags...",
removepage: "Remove Page",
save: "Save",
signupemail: "Sign Up with email",
signuptosave: "Sign up to start saving, its totally free.",
signupemail: "Sign up with email",
signuptosave: "Sign up for Pocket. Its free.",
suggestedtags: "Suggested Tags",
tagline: "Save links from Firefox to view in Pocket on any device, anytime.",
tagline: "Save articles and videos from Firefox to view in Pocket on any device, any time.",
taglinestory_one: "Click the Pocket Button to save any article, video or page from Firefox.",
taglinestory_two: "View in Pocket on any device, any time.",
tagssaved: "Tags Added",
signupfirefox: "Sign Up with Firefox",
signinfirefox: "Sign in with Firefox",
signupfirefox: "Sign up with Firefox",
viewlist: "View List"
}

View File

@ -18,7 +18,7 @@ var PKT_SAVED_OVERLAY = function (options)
this.dictJSON = {};
// TODO: allow the timer to be editable?
this.autocloseTiming = 3500;
this.autocloseTimingFinalState = 1500;
this.autocloseTimingFinalState = 2000;
this.mouseInside = false;
this.userTags = [];
this.cxt_suggested_available = 0;
@ -44,10 +44,8 @@ var PKT_SAVED_OVERLAY = function (options)
}
};
this.fillUserTags = function() {
console.log('start of logic for fillUserTags');
thePKT_SAVED.sendMessage("getTags",{},function(resp)
{
console.log('got a big tag response',resp);
if (typeof resp == 'object' && typeof resp.tags == 'object')
{
myself.userTags = resp.tags;
@ -62,14 +60,12 @@ var PKT_SAVED_OVERLAY = function (options)
myself.startCloseTimer();
return;
}
console.log('calling suggested tags',myself.savedUrl);
thePKT_SAVED.sendMessage("getSuggestedTags",
{
url: myself.savedUrl || window.location.toString()
}, function(resp)
{
$('.pkt_ext_suggestedtag_detail').removeClass('pkt_ext_suggestedtag_detail_loading');
console.log('got suggested tags response',resp);
if (resp.status == 'success')
{
var newtags = [];
@ -220,7 +216,6 @@ var PKT_SAVED_OVERLAY = function (options)
}
},
onReady: function() {
console.log('got to autoinput ready');
$('.token-input-dropdown').addClass('token-input-dropdown-tag');
inputwrapper.find('.token-input-input-token input').attr('placeholder',$('.tag-input').attr('placeholder')).css('width','200px');
if ($('.pkt_ext_suggestedtag_detail').length) {
@ -242,7 +237,7 @@ var PKT_SAVED_OVERLAY = function (options)
}
}).on('keypress','input',function(e) {
if (e.which == 13) {
if (Date.now() - changestamp > 250) {
if (typeof changestamp == 'undefined' || (Date.now() - changestamp > 250)) {
e.preventDefault();
myself.wrapper.find('.pkt_ext_btn').trigger('click');
}
@ -316,14 +311,12 @@ var PKT_SAVED_OVERLAY = function (options)
originaltags.push(text);
}
});
console.log('submitting addtags message');
thePKT_SAVED.sendMessage("addTags",
{
url: myself.savedUrl || window.location.toString(),
tags: originaltags
}, function(resp)
{
console.log('got a response',resp);
if (resp.status == 'success')
{
myself.showStateFinalMsg(myself.dictJSON.tagssaved);
@ -345,12 +338,10 @@ var PKT_SAVED_OVERLAY = function (options)
e.preventDefault();
myself.disableInput();
$('.pkt_ext_containersaved').find('.pkt_ext_detail h2').text(myself.dictJSON.processingremove);
console.log('processing page removal',myself.savedItemId);
thePKT_SAVED.sendMessage("deleteItem",
{
itemId: myself.savedItemId
},function(resp) {
console.log('got a removal message',resp);
if (resp.status == 'success') {
myself.showStateFinalMsg(myself.dictJSON.pageremoved);
}
@ -365,7 +356,6 @@ var PKT_SAVED_OVERLAY = function (options)
$('.pkt_ext_openpocket').click(function(e)
{
e.preventDefault();
console.log('sending new tab messsage',$(this).attr('href'));
thePKT_SAVED.sendMessage("openTabWithUrl",
{
url: $(this).attr('href'),
@ -406,7 +396,6 @@ var PKT_SAVED_OVERLAY = function (options)
});
};
this.showStateSaved = function(initobj) {
console.log('start of saved state',initobj);
this.wrapper.find('.pkt_ext_detail h2').text(this.dictJSON.pagesaved);
this.wrapper.find('.pkt_ext_btn').addClass('pkt_ext_btn_disabled');
if (typeof initobj.item == 'object')
@ -449,10 +438,17 @@ var PKT_SAVED_OVERLAY = function (options)
$(this).off('webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd');
myself.preventCloseTimerCancel = true;
myself.startCloseTimer(myself.autocloseTimingFinalState);
myself.wrapper.find('.pkt_ext_detail h2').text(msg);
});
this.wrapper.addClass('pkt_ext_container_finalstate');
this.wrapper.find('.pkt_ext_detail h2').text(msg);
};
this.showStateError = function(headline,detail) {
this.wrapper.find('.pkt_ext_detail h2').text(headline);
this.wrapper.find('.pkt_ext_detail h3').text(detail);
this.wrapper.addClass('pkt_ext_container_detailactive pkt_ext_container_finalstate pkt_ext_container_finalerrorstate');
this.preventCloseTimerCancel = true;
this.startCloseTimer(myself.autocloseTimingFinalState);
}
this.getTranslations = function()
{
var language = window.navigator.language.toLowerCase();
@ -540,7 +536,6 @@ var PKT_SAVED_OVERLAY = function (options)
PKT_SAVED_OVERLAY.prototype = {
create : function()
{
console.log('creating overlay',this.active);
if (this.active)
{
return;
@ -550,6 +545,9 @@ PKT_SAVED_OVERLAY.prototype = {
// set translations
this.getTranslations();
// set host
this.dictJSON.pockethost = this.pockethost;
// Create actual content
$('body').append(Handlebars.templates.saved_shell(this.dictJSON));
@ -568,7 +566,6 @@ PKT_SAVED_OVERLAY.prototype = {
{
if (this.premiumStatus && !$('.pkt_ext_suggestedtag_detail').length)
{
console.log('make premium');
$('body').append(Handlebars.templates.saved_premiumshell(this.dictJSON));
}
}
@ -598,11 +595,16 @@ PKT_SAVED.prototype = {
create: function() {
var myself = this;
var url = window.location.href.split('premiumStatus=');
if (url.length > 1)
var url = window.location.href.match(/premiumStatus=([\w|\d|\.]*)&?/);
if (url && url.length > 1)
{
myself.overlay.premiumStatus = (url[1] == '1');
}
var host = window.location.href.match(/pockethost=([\w|\.]*)&?/);
if (host && host.length > 1)
{
myself.overlay.pockethost = host[1];
}
myself.overlay.create();
// tell back end we're ready
@ -611,10 +613,14 @@ PKT_SAVED.prototype = {
// wait confirmation of save before flipping to final saved state
thePKT_SAVED.addMessageListener("saveLink",function(resp)
{
console.log('sweet, switch to full mode because of registered hit',resp);
if (resp.status == 'error') {
myself.overlay.showStateError(myself.overlay.dictJSON.pagenotsaved,myself.overlay.dictJSON.onlylinkssaved);
return;
}
myself.overlay.showStateSaved(resp);
});
}
}

View File

@ -19,12 +19,14 @@ var PKT_SIGNUP_OVERLAY = function (options)
this.closeValid = true;
this.mouseInside = false;
this.autocloseTimer = null;
this.variant = "";
this.pockethost = "getpocket.com";
this.fxasignedin = false;
this.dictJSON = {};
this.initCloseTabEvents = function() {
$('.btn,.alreadyhave > a').click(function(e)
$('.btn,.pkt_ext_learnmore,.alreadyhave > a').click(function(e)
{
e.preventDefault();
console.log('sending new tab messsage',$(this).attr('href'));
thePKT_SIGNUP.sendMessage("openTabWithUrl",
{
url: $(this).attr('href'),
@ -144,6 +146,22 @@ PKT_SIGNUP_OVERLAY.prototype = {
{
var myself = this;
var variant = window.location.href.match(/variant=([\w|\.]*)&?/);
if (variant && variant.length > 1)
{
this.variant = variant[1];
}
var fxasignedin = window.location.href.match(/fxasignedin=([\w|\d|\.]*)&?/);
if (fxasignedin && fxasignedin.length > 1)
{
this.fxasignedin = (fxasignedin[1] == '1');
}
var host = window.location.href.match(/pockethost=([\w|\.]*)&?/);
if (host && host.length > 1)
{
this.pockethost = host[1];
}
if (this.active)
{
return;
@ -151,10 +169,20 @@ PKT_SIGNUP_OVERLAY.prototype = {
this.active = true;
// set translations
myself.getTranslations();
this.getTranslations();
this.dictJSON.fxasignedin = this.fxasignedin ? 1 : 0;
this.dictJSON.variant = (this.variant ? this.variant : 'undefined');
this.dictJSON.pockethost = this.pockethost;
// Create actual content
$('body').append(Handlebars.templates.signup_shell(this.dictJSON));
if (this.variant == 'storyboard')
{
$('body').append(Handlebars.templates.signupstoryboard_shell(this.dictJSON));
}
else
{
$('body').append(Handlebars.templates.signup_shell(this.dictJSON));
}
// tell background we're ready
thePKT_SIGNUP.sendMessage("show");

View File

@ -10,9 +10,11 @@ templates['saved_shell'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":fun
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return "<div class=\"pkt_ext_initload\"> \n <div class=\"pkt_ext_loadingspinner\"><div></div></div>\n</div> \n<div class=\"pkt_ext_detail\"> \n <div class=\"pkt_ext_logo\"></div>\n <div class=\"pkt_ext_topdetail\">\n <h2>"
+ escapeExpression(((helper = (helper = helpers.pagesaved || (depth0 != null ? depth0.pagesaved : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pagesaved","hash":{},"data":data}) : helper)))
+ "</h2>\n <nav class=\"pkt_ext_item_actions pkt_ext_cf\">\n <ul>\n <li><a class=\"pkt_ext_removeitem\" href=\"#\">"
+ "</h2>\n <h3 class=\"pkt_ext_errordetail\"></h3>\n <nav class=\"pkt_ext_item_actions pkt_ext_cf\">\n <ul>\n <li><a class=\"pkt_ext_removeitem\" href=\"#\">"
+ escapeExpression(((helper = (helper = helpers.removepage || (depth0 != null ? depth0.removepage : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"removepage","hash":{},"data":data}) : helper)))
+ "</a></li>\n <li class=\"pkt_ext_actions_separator\"></li> \n <li><a class=\"pkt_ext_openpocket\" href=\"http://firefox.dev.readitlater.com/a\" target=\"_blank\">"
+ "</a></li>\n <li class=\"pkt_ext_actions_separator\"></li> \n <li><a class=\"pkt_ext_openpocket\" href=\"http://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/a?src=ff_ext_saved\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.viewlist || (depth0 != null ? depth0.viewlist : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"viewlist","hash":{},"data":data}) : helper)))
+ "</a></li>\n </ul>\n </nav> \n </div>\n <p class=\"pkt_ext_edit_msg\"></p> \n <div class=\"pkt_ext_tag_detail pkt_ext_cf\">\n <div class=\"pkt_ext_tag_input_wrapper\">\n <div class=\"pkt_ext_tag_input_blocker\"></div>\n <input class=\"pkt_ext_tag_input\" type=\"text\" placeholder=\""
+ escapeExpression(((helper = (helper = helpers.addtags || (depth0 != null ? depth0.addtags : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"addtags","hash":{},"data":data}) : helper)))
@ -20,23 +22,101 @@ templates['saved_shell'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":fun
+ escapeExpression(((helper = (helper = helpers.save || (depth0 != null ? depth0.save : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"save","hash":{},"data":data}) : helper)))
+ "</a>\n </div>\n</div>";
},"useData":true});
templates['signup_shell'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
templates['signup_shell'] = template({"1":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return "<h4 class=\"pkt_ext_introrecommend\">Recommended by Firefox</h4>\n<div class=\"pkt_ext_introdetail\">\n <h2 class=\"pkt_ext_logo\">Pocket</h2>\n <div class=\"pkt_ext_introimg\"></div>\n <p class=\"pkt_ext_tagline\">"
+ escapeExpression(((helper = (helper = helpers.tagline || (depth0 != null ? depth0.tagline : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"tagline","hash":{},"data":data}) : helper)))
+ "</p>\n <p><a class=\"pkt_ext_learnmore\" href=\"http://getpocket.com\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.learnmore || (depth0 != null ? depth0.learnmore : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"learnmore","hash":{},"data":data}) : helper)))
+ "</a></p>\n</div>\n<div class=\"pkt_ext_signupdetail\">\n <h4>"
+ escapeExpression(((helper = (helper = helpers.signuptosave || (depth0 != null ? depth0.signuptosave : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signuptosave","hash":{},"data":data}) : helper)))
+ "</h4>\n <p class=\"btn-container\"><a href=\"https://firefox.dev.readitlater.com/ff_signup\" target=_blank\" class=\"btn signup-btn-firefox\"><span class=\"logo\"></span><span class=\"text\">"
return " <p class=\"btn-container\"><a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/ff_signup?s="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "&t=wlm\" target=_blank\" class=\"btn signup-btn-firefox\"><span class=\"logo\"></span><span class=\"text\">"
+ escapeExpression(((helper = (helper = helpers.signinfirefox || (depth0 != null ? depth0.signinfirefox : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signinfirefox","hash":{},"data":data}) : helper)))
+ "</span></a></p>\n";
},"3":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return " <p class=\"btn-container\"><a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/ff_signup?s="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "&t=wlm\" target=_blank\" class=\"btn signup-btn-firefox\"><span class=\"logo\"></span><span class=\"text\">"
+ escapeExpression(((helper = (helper = helpers.signupfirefox || (depth0 != null ? depth0.signupfirefox : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signupfirefox","hash":{},"data":data}) : helper)))
+ "</span></a> <a class=\"ff_signuphelp\" href=\"https://www.mozilla.org\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.help || (depth0 != null ? depth0.help : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"help","hash":{},"data":data}) : helper)))
+ "</a></p>\n <p class=\"btn-container\"><a href=\"http://firefox.dev.readitlater.com/signup?force=email&src=extension\" target=\"_blank\" class=\"btn btn-secondary signup-btn-email signup-btn-initstate\">"
+ "</span></a></p>\n <p class=\"btn-container\"><a href=\"http://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/signup?force=email&src=extension&s="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "&t=wlm\" target=\"_blank\" class=\"btn btn-secondary signup-btn-email signup-btn-initstate\">"
+ escapeExpression(((helper = (helper = helpers.signupemail || (depth0 != null ? depth0.signupemail : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signupemail","hash":{},"data":data}) : helper)))
+ "</a></p>\n <p class=\"alreadyhave\">"
+ "</a></p>\n";
},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<div class=\"pkt_ext_introdetail\">\n <h2 class=\"pkt_ext_logo\">Pocket</h2>\n <p class=\"pkt_ext_tagline\">"
+ escapeExpression(((helper = (helper = helpers.tagline || (depth0 != null ? depth0.tagline : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"tagline","hash":{},"data":data}) : helper)))
+ "</p>\n <p><a class=\"pkt_ext_learnmore\" href=\"http://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "?s="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "&t=wlm&src=ff_learn_more\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.learnmore || (depth0 != null ? depth0.learnmore : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"learnmore","hash":{},"data":data}) : helper)))
+ "</a></p>\n <div class=\"pkt_ext_introimg\"></div>\n</div>\n<div class=\"pkt_ext_signupdetail\">\n <h4>"
+ escapeExpression(((helper = (helper = helpers.signuptosave || (depth0 != null ? depth0.signuptosave : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signuptosave","hash":{},"data":data}) : helper)))
+ "</h4>\n";
stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.fxasignedin : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(3, data),"data":data});
if (stack1 != null) { buffer += stack1; }
return buffer + " <p class=\"alreadyhave\">"
+ escapeExpression(((helper = (helper = helpers.alreadyhaveacct || (depth0 != null ? depth0.alreadyhaveacct : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"alreadyhaveacct","hash":{},"data":data}) : helper)))
+ " <a href=\"http://firefox.dev.readitlater.com/login?ep=3&src=extension\" target=\"_blank\">"
+ " <a href=\"http://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/login?ep=3&src=extension&s="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "&t=wlm\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.loginnow || (depth0 != null ? depth0.loginnow : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"loginnow","hash":{},"data":data}) : helper)))
+ "</a>.</p>\n</div>";
},"useData":true});
templates['signupstoryboard_shell'] = template({"1":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return " <p class=\"btn-container\"><a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/ff_signup?s="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "&t=wlm\" target=_blank\" class=\"btn signup-btn-firefox\"><span class=\"logo\"></span><span class=\"text\">"
+ escapeExpression(((helper = (helper = helpers.signinfirefox || (depth0 != null ? depth0.signinfirefox : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signinfirefox","hash":{},"data":data}) : helper)))
+ "</span></a></p>\n";
},"3":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return " <p class=\"btn-container\"><a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/ff_signup?s="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "&t=wlm\" target=_blank\" class=\"btn signup-btn-firefox\"><span class=\"logo\"></span><span class=\"text\">"
+ escapeExpression(((helper = (helper = helpers.signupfirefox || (depth0 != null ? depth0.signupfirefox : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signupfirefox","hash":{},"data":data}) : helper)))
+ "</span></a></p>\n <p class=\"btn-container\"><a href=\"http://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/signup?force=email&src=extension&s="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "&t=wlm\" target=\"_blank\" class=\"btn btn-secondary signup-btn-email signup-btn-initstate\">"
+ escapeExpression(((helper = (helper = helpers.signupemail || (depth0 != null ? depth0.signupemail : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signupemail","hash":{},"data":data}) : helper)))
+ "</a></p>\n";
},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<div class=\"pkt_ext_introdetail pkt_ext_introdetailstoryboard\">\n <div class=\"pkt_ext_introstory pkt_ext_introstoryone\">\n <div class=\"pkt_ext_introstory_text\">\n <p class=\"pkt_ext_tagline\">"
+ escapeExpression(((helper = (helper = helpers.taglinestory_one || (depth0 != null ? depth0.taglinestory_one : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"taglinestory_one","hash":{},"data":data}) : helper)))
+ "</p>\n </div>\n <div class=\"pkt_ext_introstoryone_img\"></div>\n </div>\n <div class=\"pkt_ext_introstorydivider\"></div>\n <div class=\"pkt_ext_introstory pkt_ext_introstorytwo\">\n <div class=\"pkt_ext_introstory_text\">\n <p class=\"pkt_ext_tagline\">"
+ escapeExpression(((helper = (helper = helpers.taglinestory_two || (depth0 != null ? depth0.taglinestory_two : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"taglinestory_two","hash":{},"data":data}) : helper)))
+ "</p>\n <p><a class=\"pkt_ext_learnmore\" href=\"http://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "?s="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "&t=wlm&src=ff_learn_more\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.learnmore || (depth0 != null ? depth0.learnmore : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"learnmore","hash":{},"data":data}) : helper)))
+ "</a></p>\n </div>\n <div class=\"pkt_ext_introstorytwo_img\"></div>\n </div>\n</div>\n<div class=\"pkt_ext_signupdetail\">\n <h4>"
+ escapeExpression(((helper = (helper = helpers.signuptosave || (depth0 != null ? depth0.signuptosave : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signuptosave","hash":{},"data":data}) : helper)))
+ "</h4>\n";
stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.fxasignedin : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(3, data),"data":data});
if (stack1 != null) { buffer += stack1; }
return buffer + " <p class=\"alreadyhave\">"
+ escapeExpression(((helper = (helper = helpers.alreadyhaveacct || (depth0 != null ? depth0.alreadyhaveacct : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"alreadyhaveacct","hash":{},"data":data}) : helper)))
+ " <a href=\"http://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/login?ep=3&src=extension&s="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "&t=wlm\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.loginnow || (depth0 != null ? depth0.loginnow : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"loginnow","hash":{},"data":data}) : helper)))
+ "</a>.</p>\n</div>";
},"useData":true});

View File

@ -9,7 +9,7 @@ to the license set forth below, "Pocket," "Read It Later" and the Pocket icon
and logos (collectively, the “Pocket Marks”) are registered and common law
trademarks of Read It Later, Inc. This means that, while you have considerable
freedom to redistribute and modify the Software, there are tight restrictions
on your ability to use the Pocket Marks. This license does not grant you any
on your ability to use the Pocket Marks. This license does not grant you any
rights to use the Pocket Marks except as they are embodied in the Software.
---

View File

@ -5,11 +5,12 @@
<div class="pkt_ext_logo"></div>
<div class="pkt_ext_topdetail">
<h2>{{pagesaved}}</h2>
<h3 class="pkt_ext_errordetail"></h3>
<nav class="pkt_ext_item_actions pkt_ext_cf">
<ul>
<li><a class="pkt_ext_removeitem" href="#">{{removepage}}</a></li>
<li class="pkt_ext_actions_separator"></li>
<li><a class="pkt_ext_openpocket" href="http://firefox.dev.readitlater.com/a" target="_blank">{{viewlist}}</a></li>
<li><a class="pkt_ext_openpocket" href="http://{{pockethost}}/a?src=ff_ext_saved" target="_blank">{{viewlist}}</a></li>
</ul>
</nav>
</div>

View File

@ -1,13 +1,16 @@
<h4 class="pkt_ext_introrecommend">Recommended by Firefox</h4>
<div class="pkt_ext_introdetail">
<h2 class="pkt_ext_logo">Pocket</h2>
<div class="pkt_ext_introimg"></div>
<p class="pkt_ext_tagline">{{tagline}}</p>
<p><a class="pkt_ext_learnmore" href="http://getpocket.com" target="_blank">{{learnmore}}</a></p>
<p><a class="pkt_ext_learnmore" href="http://{{pockethost}}?s={{variant}}&t=wlm&src=ff_learn_more" target="_blank">{{learnmore}}</a></p>
<div class="pkt_ext_introimg"></div>
</div>
<div class="pkt_ext_signupdetail">
<h4>{{signuptosave}}</h4>
<p class="btn-container"><a href="https://firefox.dev.readitlater.com/ff_signup" target=_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signupfirefox}}</span></a> <a class="ff_signuphelp" href="https://www.mozilla.org" target="_blank">{{help}}</a></p>
<p class="btn-container"><a href="http://firefox.dev.readitlater.com/signup?force=email&src=extension" target="_blank" class="btn btn-secondary signup-btn-email signup-btn-initstate">{{signupemail}}</a></p>
<p class="alreadyhave">{{alreadyhaveacct}} <a href="http://firefox.dev.readitlater.com/login?ep=3&src=extension" target="_blank">{{loginnow}}</a>.</p>
{{#if fxasignedin}}
<p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s={{variant}}&t=wlm" target=_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signinfirefox}}</span></a></p>
{{else}}
<p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s={{variant}}&t=wlm" target=_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signupfirefox}}</span></a></p>
<p class="btn-container"><a href="http://{{pockethost}}/signup?force=email&src=extension&s={{variant}}&t=wlm" target="_blank" class="btn btn-secondary signup-btn-email signup-btn-initstate">{{signupemail}}</a></p>
{{/if}}
<p class="alreadyhave">{{alreadyhaveacct}} <a href="http://{{pockethost}}/login?ep=3&src=extension&s={{variant}}&t=wlm" target="_blank">{{loginnow}}</a>.</p>
</div>

View File

@ -0,0 +1,26 @@
<div class="pkt_ext_introdetail pkt_ext_introdetailstoryboard">
<div class="pkt_ext_introstory pkt_ext_introstoryone">
<div class="pkt_ext_introstory_text">
<p class="pkt_ext_tagline">{{taglinestory_one}}</p>
</div>
<div class="pkt_ext_introstoryone_img"></div>
</div>
<div class="pkt_ext_introstorydivider"></div>
<div class="pkt_ext_introstory pkt_ext_introstorytwo">
<div class="pkt_ext_introstory_text">
<p class="pkt_ext_tagline">{{taglinestory_two}}</p>
<p><a class="pkt_ext_learnmore" href="http://{{pockethost}}?s={{variant}}&t=wlm&src=ff_learn_more" target="_blank">{{learnmore}}</a></p>
</div>
<div class="pkt_ext_introstorytwo_img"></div>
</div>
</div>
<div class="pkt_ext_signupdetail">
<h4>{{signuptosave}}</h4>
{{#if fxasignedin}}
<p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s={{variant}}&t=wlm" target=_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signinfirefox}}</span></a></p>
{{else}}
<p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s={{variant}}&t=wlm" target=_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signupfirefox}}</span></a></p>
<p class="btn-container"><a href="http://{{pockethost}}/signup?force=email&src=extension&s={{variant}}&t=wlm" target="_blank" class="btn btn-secondary signup-btn-email signup-btn-initstate">{{signupemail}}</a></p>
{{/if}}
<p class="alreadyhave">{{alreadyhaveacct}} <a href="http://{{pockethost}}/login?ep=3&src=extension&s={{variant}}&t=wlm" target="_blank">{{loginnow}}</a>.</p>
</div>

View File

@ -50,7 +50,8 @@ var pktApi = (function() {
// Base url for all api calls
// TODO: This is a dev server and will be changed before launch
var pocketAPIhost = Services.prefs.getCharPref("browser.pocket.hostname");
var pocketAPIhost = Services.prefs.getCharPref("browser.pocket.api");
var pocketSiteHost = Services.prefs.getCharPref("browser.pocket.site");
// Base url for all api calls
var baseAPIUrl = "https://" + pocketAPIhost + "/v3";
@ -139,7 +140,7 @@ var pktApi = (function() {
function getCookiesFromPocket() {
var cookieManager = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
var pocketCookies = cookieManager.getCookiesFromHost(pocketAPIhost);
var pocketCookies = cookieManager.getCookiesFromHost(pocketSiteHost);
var cookies = {};
while (pocketCookies.hasMoreElements()) {
var cookie = pocketCookies.getNext().QueryInterface(Ci.nsICookie2);
@ -225,6 +226,12 @@ var pktApi = (function() {
// TODO: Better error handling
if (options.error) {
// In case the user did revoke the access token or it's not
// valid anymore clear the user data
if (request.status === 401) {
clearUserData();
}
// Check to handle Pocket error
var errorMessage = request.getResponseHeader("X-Error");
if (typeof errorMessage !== "undefined") {
@ -238,9 +245,6 @@ var pktApi = (function() {
}
};
// TODO - do we want to pass a special user agent?
//request.setRequestHeader("User-Agent" , 'Pocket Firefox ' + this.APP.v);
// Set headers
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.setRequestHeader('X-Accept',' application/json');
@ -268,6 +272,7 @@ var pktApi = (function() {
setSetting("tags", undefined);
setSetting("usedTags", undefined);
setSetting("fsv1", undefined);
}
/**
@ -321,8 +326,6 @@ var pktApi = (function() {
},
error: options.error
});
return sendAction(action, options);
}
/**
@ -444,7 +447,7 @@ var pktApi = (function() {
var tagToSave = tags[i].trim();
var newUsedTagObject = {
"tag": tagToSave,
"timestamp": new Date()
"timestamp": new Date().getTime()
};
usedTags[tagToSave] = newUsedTagObject;
}
@ -488,9 +491,9 @@ var pktApi = (function() {
}
// Sort usedTagsObjectArray based on timestamp
usedTagsObjectArray.sort(function(a, b) {
a = new Date(a.timestamp);
b = new Date(b.timestamp);
usedTagsObjectArray.sort(function(usedTagA, usedTagB) {
var a = usedTagA.timestamp;
var b = usedTagB.timestamp;
return a < b ? -1 : a > b ? 1 : 0;
});
@ -563,6 +566,26 @@ var pktApi = (function() {
});
}
/**
* Helper function to get current signup AB group the user is in
*/
function getSignupAB() {
if (!getSetting('signupAB'))
{
var rand = (Math.floor(Math.random()*2+1));
if (rand == 2)
{
setSetting('signupAB','storyboard');
}
else
{
setSetting('signupAB','hero');
}
}
return getSetting('signupAB');
}
/**
* Public functions
*/
@ -576,6 +599,7 @@ var pktApi = (function() {
getTags: getTags,
isPremiumUser: isPremiumUser,
getSuggestedTagsForItem: getSuggestedTagsForItem,
getSuggestedTagsForURL: getSuggestedTagsForURL
getSuggestedTagsForURL: getSuggestedTagsForURL,
getSignupAB: getSignupAB
};
}());
}());

View File

@ -71,22 +71,6 @@ this.SessionFile = {
write: function (aData) {
return SessionFileInternal.write(aData);
},
/**
* Gather telemetry statistics.
*
*
* Most of the work is done off the main thread but there is a main
* thread cost involved to send data to the worker thread. This method
* should therefore be called only when we know that it will not disrupt
* the user's experience, e.g. on idle-daily.
*
* @return {Promise}
* @promise {object} An object holding all the information to be submitted
* to Telemetry.
*/
gatherTelemetry: function(aData) {
return SessionFileInternal.gatherTelemetry(aData);
},
/**
* Wipe the contents of the session file, asynchronously.
*/
@ -268,14 +252,6 @@ let SessionFileInternal = {
return result;
}),
gatherTelemetry: function(aStateString) {
return Task.spawn(function() {
let msg = yield SessionWorker.post("gatherTelemetry", [aStateString]);
this._recordTelemetry(msg.telemetry);
throw new Task.Result(msg.telemetry);
}.bind(this));
},
write: function (aData) {
if (RunState.isClosed) {
return Promise.reject(new Error("SessionFile is closed"));
@ -289,25 +265,11 @@ let SessionFileInternal = {
RunState.setClosed();
}
let refObj = {};
let name = "FX_SESSION_RESTORE_WRITE_FILE_LONGEST_OP_MS";
let performShutdownCleanup = isFinalWrite &&
!sessionStartup.isAutomaticRestoreEnabled();
let promise = new Promise(resolve => {
// Start measuring main thread impact.
TelemetryStopwatch.start(name, refObj);
let performShutdownCleanup = isFinalWrite &&
!sessionStartup.isAutomaticRestoreEnabled();
let options = {isFinalWrite, performShutdownCleanup};
try {
resolve(SessionWorker.post("write", [aData, options]));
} finally {
// Record how long we stopped the main thread.
TelemetryStopwatch.finish(name, refObj);
}
});
let options = {isFinalWrite, performShutdownCleanup};
let promise = SessionWorker.post("write", [aData, options]);
// Wait until the write is done.
promise = promise.then(msg => {
@ -322,7 +284,6 @@ let SessionFileInternal = {
}
}, err => {
// Catch and report any errors.
TelemetryStopwatch.cancel(name, refObj);
console.error("Could not write session state file ", err, err.stack);
// By not doing anything special here we ensure that |promise| cannot
// be rejected anymore. The shutdown/cleanup code at the end of the

View File

@ -246,8 +246,6 @@ let SessionSaverInternal = {
* Write the given state object to disk.
*/
_writeState: function (state) {
stopWatchStart("WRITE_STATE_LONGEST_OP_MS");
// We update the time stamp before writing so that we don't write again
// too soon, if saving is requested before the write completes. Without
// this update we may save repeatedly if actions cause a runDelayed
@ -257,14 +255,9 @@ let SessionSaverInternal = {
// Write (atomically) to a session file, using a tmp file. Once the session
// file is successfully updated, save the time stamp of the last save and
// notify the observers.
let promise = SessionFile.write(state);
stopWatchFinish("WRITE_STATE_LONGEST_OP_MS");
promise = promise.then(() => {
return SessionFile.write(state).then(() => {
this.updateLastSaveTime();
notify(null, "sessionstore-state-write-complete");
}, console.error);
return promise;
},
};

View File

@ -30,7 +30,6 @@ const OBSERVING = [
"quit-application-requested", "browser-lastwindow-close-granted",
"quit-application", "browser:purge-session-history",
"browser:purge-domain-data",
"gather-telemetry",
"idle-daily",
];
@ -103,9 +102,6 @@ const TAB_EVENTS = [
"TabUnpinned"
];
// The number of milliseconds in a day
const MS_PER_DAY = 1000.0 * 60.0 * 60.0 * 24.0;
Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", this);
@ -591,9 +587,6 @@ let SessionStoreInternal = {
case "nsPref:changed": // catch pref changes
this.onPrefChange(aData);
break;
case "gather-telemetry":
this.onGatherTelemetry();
break;
case "idle-daily":
this.onIdleDaily();
break;
@ -1616,14 +1609,6 @@ let SessionStoreInternal = {
this._resetLocalTabRestoringState(tab);
},
onGatherTelemetry: function() {
// On the first gather-telemetry notification of the session,
// gather telemetry data.
Services.obs.removeObserver(this, "gather-telemetry");
let stateString = SessionStore.getBrowserState();
return SessionFile.gatherTelemetry(stateString);
},
// Clean up data that has been closed a long time ago.
// Do not reschedule a save. This will wait for the next regular
// save.
@ -2405,7 +2390,6 @@ let SessionStoreInternal = {
_collectWindowData: function ssi_collectWindowData(aWindow) {
if (!this._isWindowLoaded(aWindow))
return;
TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_SINGLE_WINDOW_DATA_MS");
let tabbrowser = aWindow.gBrowser;
let tabs = tabbrowser.tabs;
@ -2427,7 +2411,6 @@ let SessionStoreInternal = {
aWindow.__SS_lastSessionWindowID;
DirtyWindows.remove(aWindow);
TelemetryStopwatch.finish("FX_SESSION_RESTORE_COLLECT_SINGLE_WINDOW_DATA_MS");
},
/* ........ Restoring Functionality .............. */
@ -2754,7 +2737,8 @@ let SessionStoreInternal = {
// fill the array with at least empty tabData objects until |_tPos| or
// we'll end up with |null| entries.
for (let tab of Array.slice(tabbrowser.tabs, 0, tab._tPos)) {
this._windows[window.__SSi].tabs.push(TabState.collect(tab));
let emptyState = {entries: [], lastAccessed: tab.lastAccessed};
this._windows[window.__SSi].tabs.push(emptyState);
}
// Update the tab state in case we shut down without being notified.
@ -3076,14 +3060,6 @@ let SessionStoreInternal = {
// Attempt to load the session start time from the session state
if (state.session && state.session.startTime) {
this._sessionStartTime = state.session.startTime;
// ms to days
let sessionLength = (Date.now() - this._sessionStartTime) / MS_PER_DAY;
if (sessionLength > 0) {
// Submit the session length telemetry measurement
Services.telemetry.getHistogramById("FX_SESSION_RESTORE_SESSION_LENGTH").add(sessionLength);
}
}
},

View File

@ -283,16 +283,6 @@ let Agent = {
};
},
/**
* Extract all sorts of useful statistics from a state string,
* for use with Telemetry.
*
* @return {object}
*/
gatherTelemetry: function (stateString) {
return Statistics.collect(stateString);
},
/**
* Wipes all files holding session data from disk.
*/
@ -388,131 +378,3 @@ function isNoSuchFileEx(aReason) {
function getByteLength(str) {
return Encoder.encode(JSON.stringify(str)).byteLength;
}
/**
* Tools for gathering statistics on a state string.
*/
let Statistics = {
collect: function(stateString) {
let start = Date.now();
let TOTAL_PREFIX = "FX_SESSION_RESTORE_TOTAL_";
let INDIVIDUAL_PREFIX = "FX_SESSION_RESTORE_INDIVIDUAL_";
let SIZE_SUFFIX = "_SIZE_BYTES";
let state = JSON.parse(stateString);
// Gather all data
let subsets = {};
this.gatherSimpleData(state, subsets);
this.gatherComplexData(state, subsets);
// Extract telemetry
let telemetry = {};
for (let k of Object.keys(subsets)) {
let obj = subsets[k];
telemetry[TOTAL_PREFIX + k + SIZE_SUFFIX] = getByteLength(obj);
if (Array.isArray(obj)) {
let size = obj.map(getByteLength);
telemetry[INDIVIDUAL_PREFIX + k + SIZE_SUFFIX] = size;
}
}
let stop = Date.now();
telemetry["FX_SESSION_RESTORE_EXTRACTING_STATISTICS_DURATION_MS"] = stop - start;
return {
telemetry: telemetry
};
},
/**
* Collect data that doesn't require a recursive walk through the
* data structure.
*/
gatherSimpleData: function(state, subsets) {
// The subset of sessionstore.js dealing with open windows
subsets.OPEN_WINDOWS = state.windows;
// The subset of sessionstore.js dealing with closed windows
subsets.CLOSED_WINDOWS = state._closedWindows;
// The subset of sessionstore.js dealing with closed tabs
// in open windows
subsets.CLOSED_TABS_IN_OPEN_WINDOWS = [];
// The subset of sessionstore.js dealing with cookies
// in both open and closed windows
subsets.COOKIES = [];
for (let winData of state.windows) {
let closedTabs = winData._closedTabs || [];
subsets.CLOSED_TABS_IN_OPEN_WINDOWS.push(...closedTabs);
let cookies = winData.cookies || [];
subsets.COOKIES.push(...cookies);
}
for (let winData of state._closedWindows) {
let cookies = winData.cookies || [];
subsets.COOKIES.push(...cookies);
}
},
/**
* Walk through a data structure, recursively.
*
* @param {object} root The object from which to start walking.
* @param {function(key, value)} cb Callback, called for each
* item except the root. Returns |true| to walk the subtree rooted
* at |value|, |false| otherwise */
walk: function(root, cb) {
if (!root || typeof root !== "object") {
return;
}
for (let k of Object.keys(root)) {
let obj = root[k];
let stepIn = cb(k, obj);
if (stepIn) {
this.walk(obj, cb);
}
}
},
/**
* Collect data that requires walking through the data structure
*/
gatherComplexData: function(state, subsets) {
// The subset of sessionstore.js dealing with DOM storage
subsets.DOM_STORAGE = [];
// The subset of sessionstore.js storing form data
subsets.FORMDATA = [];
// The subset of sessionstore.js storing history
subsets.HISTORY = [];
this.walk(state, function(k, value) {
let dest;
switch (k) {
case "entries":
subsets.HISTORY.push(value);
return true;
case "storage":
subsets.DOM_STORAGE.push(value);
// Never visit storage, it's full of weird stuff
return false;
case "formdata":
subsets.FORMDATA.push(value);
// Never visit formdata, it's full of weird stuff
return false;
case "cookies": // Don't visit these places, they are full of weird stuff
case "extData":
return false;
default:
return true;
}
});
return subsets;
},
};

View File

@ -265,6 +265,9 @@ let SessionHistoryListener = {
if (!SessionHistory.isEmpty(docShell)) {
this.collect();
}
// Listen for page title changes.
addEventListener("DOMTitleChanged", this);
},
uninit: function () {
@ -280,6 +283,10 @@ let SessionHistoryListener = {
}
},
handleEvent(event) {
this.collect();
},
onFrameTreeCollected: function () {
this.collect();
},

View File

@ -88,6 +88,7 @@ skip-if = buildapp == 'mulet'
[browser_history_persist.js]
[browser_label_and_icon.js]
[browser_merge_closed_tabs.js]
[browser_page_title.js]
[browser_pageStyle.js]
[browser_privatetabs.js]
[browser_replace_load.js]
@ -101,7 +102,6 @@ skip-if = e10s
skip-if = e10s # See bug 918634
[browser_switch_remoteness.js]
run-if = e10s
[browser_telemetry.js]
[browser_upgrade_backup.js]
[browser_windowRestore_perwindowpb.js]
[browser_248970_b_perwindowpb.js]

View File

@ -0,0 +1,38 @@
"use strict";
const URL = "data:text/html,<title>initial title</title>";
add_task(function* () {
// Create a new tab.
let tab = gBrowser.addTab(URL);
yield promiseBrowserLoaded(tab.linkedBrowser);
// Remove the tab.
yield promiseRemoveTab(tab);
// Check the title.
let [{state: {entries}}] = JSON.parse(ss.getClosedTabData(window));
is(entries[0].title, "initial title", "correct title");
});
add_task(function* () {
// Create a new tab.
let tab = gBrowser.addTab(URL);
let browser = tab.linkedBrowser;
yield promiseBrowserLoaded(browser);
// Flush to ensure we collected the initial title.
TabState.flush(browser);
// Set a new title.
yield ContentTask.spawn(browser, null, function* () {
content.document.title = "new title";
});
// Remove the tab.
yield promiseRemoveTab(tab);
// Check the title.
let [{state: {entries}}] = JSON.parse(ss.getClosedTabData(window));
is(entries[0].title, "new title", "correct title");
});

View File

@ -1,270 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
let tmp = {};
Cu.import("resource:///modules/sessionstore/SessionFile.jsm", tmp);
let {SessionFile} = tmp;
// Shortcuts for histogram names
let Keys = {};
for (let k of ["HISTORY", "FORMDATA", "OPEN_WINDOWS", "CLOSED_WINDOWS", "CLOSED_TABS_IN_OPEN_WINDOWS", "DOM_STORAGE"]) {
Keys[k] = "FX_SESSION_RESTORE_TOTAL_" + k + "_SIZE_BYTES";
}
function lt(a, b, message) {
isnot(a, undefined, message + " (sanity check)");
isnot(b, undefined, message + " (sanity check)");
ok(a < b, message + " ( " + a + " < " + b + ")");
}
function gt(a, b, message) {
isnot(a, undefined, message + " (sanity check)");
isnot(b, undefined, message + " (sanity check)");
ok(a > b, message + " ( " + a + " > " + b + ")");
}
add_task(function init() {
forgetClosedWindows();
for (let i = ss.getClosedTabCount(window) - 1; i >= 0; --i) {
ss.forgetClosedTab(window, i);
}
});
/**
* Test that Telemetry collection doesn't cause any error.
*/
add_task(function() {
info("Checking a little bit of consistency");
let statistics = yield promiseStats();
for (let k of Object.keys(statistics)) {
let data = statistics[k];
info("Data for " + k + ": " + data);
if (Array.isArray(data)) {
ok(data.every(x => x >= 0), "Data for " + k + " is >= 0");
} else {
ok(data >= 0, "Data for " + k + " is >= 0");
}
}
});
/**
* Test HISTORY key.
*/
add_task(function history() {
let KEY = Keys.HISTORY;
let tab = gBrowser.addTab("http://example.org:80/?");
yield promiseBrowserLoaded(tab.linkedBrowser);
try {
TabState.flush(tab.linkedBrowser);
let statistics = yield promiseStats();
info("Now changing history");
tab.linkedBrowser.loadURI("http://example.org:80/1");
yield promiseBrowserLoaded(tab.linkedBrowser);
TabState.flush(tab.linkedBrowser);
let statistics2 = yield promiseStats();
// We have changed history, so it must have increased
isnot(statistics[KEY], undefined, "Key was defined");
isnot(statistics2[KEY], undefined, "Key is still defined");
gt(statistics2[KEY], statistics[KEY], "The total size of HISTORY has increased");
// Almost nothing else should
for (let k of ["FORMDATA", "DOM_STORAGE", "CLOSED_WINDOWS", "CLOSED_TABS_IN_OPEN_WINDOWS"]) {
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
}
} finally {
if (tab) {
yield promiseRemoveTab(tab);
}
}
});
/**
* Test CLOSED_TABS_IN_OPEN_WINDOWS key.
*/
add_task(function close_tab() {
let KEY = Keys.CLOSED_TABS_IN_OPEN_WINDOWS;
let tab = gBrowser.addTab("http://example.org:80/?close_tab");
yield promiseBrowserLoaded(tab.linkedBrowser);
try {
TabState.flush(tab.linkedBrowser);
let statistics = yield promiseStats();
info("Now closing a tab");
yield promiseRemoveTab(tab);
tab = null;
let statistics2 = yield promiseStats();
isnot(statistics[KEY], undefined, "Key was defined");
isnot(statistics2[KEY], undefined, "Key is still defined");
gt(statistics2[KEY], statistics[KEY], "The total size of CLOSED_TABS_IN_OPEN_WINDOWS has increased");
// Almost nothing else should change
for (let k of ["FORMDATA", "DOM_STORAGE", "CLOSED_WINDOWS"]) {
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
}
} finally {
if (tab) {
yield promiseRemoveTab(tab);
}
}
});
/**
* Test OPEN_WINDOWS key.
*/
add_task(function open_window() {
let KEY = Keys.OPEN_WINDOWS;
let win;
try {
let statistics = yield promiseStats();
win = yield promiseNewWindowLoaded("http://example.org:80/?open_window");
let statistics2 = yield promiseStats();
isnot(statistics[KEY], undefined, "Key was defined");
isnot(statistics2[KEY], undefined, "Key is still defined");
gt(statistics2[KEY], statistics[KEY], "The total size of OPEN_WINDOWS has increased");
// Almost nothing else should change
for (let k of ["FORMDATA", "DOM_STORAGE", "CLOSED_WINDOWS", "CLOSED_TABS_IN_OPEN_WINDOWS"]) {
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
}
} finally {
if (win) {
yield promiseWindowClosed(win);
}
}
});
/**
* Test CLOSED_WINDOWS key.
*/
add_task(function close_window() {
let KEY = Keys.CLOSED_WINDOWS;
let win = yield promiseNewWindowLoaded("http://example.org:80/?close_window");
// We need to add something to the window, otherwise it won't be saved
let tab = win.gBrowser.addTab("http://example.org:80/?close_tab");
yield promiseBrowserLoaded(tab.linkedBrowser);
try {
let statistics = yield promiseStats();
yield promiseWindowClosed(win);
win = null;
let statistics2 = yield promiseStats();
isnot(statistics[KEY], undefined, "Key was defined");
isnot(statistics2[KEY], undefined, "Key is still defined");
gt(statistics2[KEY], statistics[KEY], "The total size of CLOSED_WINDOWS has increased");
lt(statistics2[Keys.OPEN_WINDOWS], statistics[Keys.OPEN_WINDOWS], "The total size of OPEN_WINDOWS has decreased");
// Almost nothing else should change
for (let k of ["FORMDATA", "DOM_STORAGE", "CLOSED_TABS_IN_OPEN_WINDOWS"]) {
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
}
} finally {
if (win) {
yield promiseWindowClosed(win);
}
}
});
/**
* Test DOM_STORAGE key.
*/
add_task(function dom_storage() {
let KEY = Keys.DOM_STORAGE;
let tab = gBrowser.addTab("http://example.org:80/?dom_storage");
yield promiseBrowserLoaded(tab.linkedBrowser);
try {
TabState.flush(tab.linkedBrowser);
let statistics = yield promiseStats();
info("Now adding some storage");
yield modifySessionStorage(tab.linkedBrowser, {foo: "bar"});
TabState.flush(tab.linkedBrowser);
let statistics2 = yield promiseStats();
isnot(statistics[KEY], undefined, "Key was defined");
isnot(statistics2[KEY], undefined, "Key is still defined");
gt(statistics2[KEY], statistics[KEY], "The total size of DOM_STORAGE has increased");
// Almost nothing else should change
for (let k of ["CLOSED_TABS_IN_OPEN_WINDOWS", "FORMDATA", "CLOSED_WINDOWS"]) {
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
}
} finally {
if (tab) {
yield promiseRemoveTab(tab);
}
}
});
/**
* Test FORMDATA key.
*/
add_task(function formdata() {
let KEY = Keys.FORMDATA;
let tab = gBrowser.addTab("data:text/html;charset=utf-8,<input%20id='input'>");
yield promiseBrowserLoaded(tab.linkedBrowser);
try {
TabState.flush(tab.linkedBrowser);
let statistics = yield promiseStats();
info("Now changing form data");
yield setInputValue(tab.linkedBrowser, {id: "input", value: "This is some form data"});
TabState.flush(tab.linkedBrowser);
let statistics2 = yield promiseStats();
isnot(statistics[KEY], undefined, "Key was defined");
isnot(statistics2[KEY], undefined, "Key is still defined");
gt(statistics2[KEY], statistics[KEY], "The total size of FORMDATA has increased");
// Almost nothing else should
for (let k of ["DOM_STORAGE", "CLOSED_WINDOWS", "CLOSED_TABS_IN_OPEN_WINDOWS"]) {
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
}
} finally {
if (tab) {
yield promiseRemoveTab(tab);
}
}
});
add_task(function* test_sessionRestoreInit() {
let info = Cc["@mozilla.org/toolkit/app-startup;1"].
getService(Ci.nsIAppStartup).
getStartupInfo();
ok(info.sessionRestoreInit > info.process, "sessionRestoreInit is after process creation");
ok(info.sessionRestoreInit <
Date.now() + 10000 /* Date.now() is non-monotonic, let's play it paranoid*/,
"sessionRestoreInit is before now");
});
/**
* Get the latest statistics.
*/
function promiseStats() {
let state = ss.getBrowserState();
info("Stats: " + state);
return SessionFile.gatherTelemetry(state);
}
function modifySessionStorage(browser, data) {
browser.messageManager.sendAsyncMessage("ss-test:modifySessionStorage", data);
return promiseContentMessage(browser, "ss-test:MozStorageChanged");
}
function setInputValue(browser, data) {
return sendMessage(browser, "ss-test:setInputValue", data);
}

View File

@ -528,6 +528,8 @@ for (let name of FORM_HELPERS) {
this[name] = (browser, data) => sendMessage(browser, msg, data);
}
// Removes the given tab immediately and returns a promise that resolves when
// all pending status updates (messages) of the closing tab have been received.
function promiseRemoveTab(tab) {
return BrowserTestUtils.removeTab(tab);
}

View File

@ -465,10 +465,6 @@ let UI = {
});
this._reorderTabItemsOnShow = [];
#ifdef XP_WIN
// Restore the full height when showing TabView
gTabViewFrame.style.marginTop = "";
#endif
gTabViewDeck.selectedPanel = gTabViewFrame;
gWindow.TabsInTitlebar.allowedBy("tabview-open", false);
gTabViewFrame.contentWindow.focus();
@ -543,12 +539,6 @@ let UI = {
});
this._reorderTabsOnHide = [];
#ifdef XP_WIN
// Push the top of TabView frame to behind the tabbrowser, so glass can show
// XXX bug 586679: avoid shrinking the iframe and squishing iframe contents
// as well as avoiding the flash of black as we animate out
gTabViewFrame.style.marginTop = gBrowser.boxObject.y + "px";
#endif
gTabViewDeck.selectedPanel = gBrowserPanel;
gWindow.TabsInTitlebar.allowedBy("tabview-open", true);
gBrowser.selectedBrowser.focus();

View File

@ -223,10 +223,17 @@ MemoryFrontFacade.prototype = {
yield this.attach();
let startTime = yield this.startRecordingAllocations({
probability: options.allocationsSampleProbability,
maxLogLength: options.allocationsMaxLogLength
});
// Reconstruct our options because the server actor fails when being passed
// undefined values in objects.
let allocationOptions = {};
if (options.allocationsSampleProbability !== void 0) {
allocationOptions.probability = options.allocationsSampleProbability;
}
if (options.allocationsMaxLogLength !== void 0) {
allocationOptions.maxLogLength = options.allocationsMaxLogLength;
}
let startTime = yield this.startRecordingAllocations(allocationOptions);
yield this._pullAllocationSites();

View File

@ -27,9 +27,8 @@ const DEFAULT_ALLOCATION_SITES_PULL_TIMEOUT = 200; // ms
// Events to pipe from PerformanceActorsConnection to the PerformanceFront
const CONNECTION_PIPE_EVENTS = [
"console-profile-start", "console-profile-ending", "console-profile-end",
"timeline-data", "profiler-already-active", "profiler-activated",
"recording-started", "recording-stopped"
"recording-starting", "recording-started", "recording-stopping", "recording-stopped"
];
/**
@ -227,8 +226,6 @@ PerformanceActorsConnection.prototype = {
console: true,
label: profileLabel
}));
this.emit("console-profile-start", model);
}),
/**
@ -271,9 +268,7 @@ PerformanceActorsConnection.prototype = {
return;
}
this.emit("console-profile-ending", model);
yield this.stopRecording(model);
this.emit("console-profile-end", model);
}),
/**
@ -309,6 +304,8 @@ PerformanceActorsConnection.prototype = {
*/
startRecording: Task.async(function*(options = {}) {
let model = new RecordingModel(options);
this.emit("recording-starting", model);
// All actors are started asynchronously over the remote debugging protocol.
// Get the corresponding start times from each one of them.
// The timeline and memory actors are target-dependent, so start those as well,
@ -343,6 +340,13 @@ PerformanceActorsConnection.prototype = {
return;
}
// Flag the recording as no longer recording, so that `model.isRecording()`
// is false. Do this before we fetch all the data, and then subsequently
// the recording can be considered "completed".
let endTime = Date.now();
model._onStoppingRecording(endTime);
this.emit("recording-stopping", model);
// Currently there are two ways profiles stop recording. Either manually in the
// performance tool, or via console.profileEnd. Once a recording is done,
// we want to deliver the model to the performance tool (either as a return
@ -485,7 +489,9 @@ PerformanceFront.prototype = {
}
let actor = this._connection[`_${actorName}`];
return actor[method].apply(actor, args);
}
},
toString: () => "[object PerformanceFront]"
};
/**

View File

@ -38,6 +38,7 @@ RecordingModel.prototype = {
_console: false,
_imported: false,
_recording: false,
_completed: false,
_profilerStartTime: 0,
_timelineStartTime: 0,
_memoryStartTime: 0,
@ -94,7 +95,7 @@ RecordingModel.prototype = {
// However, we also want to update the view with the elapsed time
// even when the actor is not generating data. To do this we get
// the local time and use it to compute a reasonable elapsed time.
this._localStartTime = Date.now()
this._localStartTime = Date.now();
this._profilerStartTime = info.profilerStartTime;
this._timelineStartTime = info.timelineStartTime;
@ -108,19 +109,30 @@ RecordingModel.prototype = {
this._allocations = { sites: [], timestamps: [], frames: [], counts: [] };
},
/**
* Called when the signal was sent to the front to no longer record more
* data, and begin fetching the data. There's some delay during fetching,
* even though the recording is stopped, the model is not yet completed until
* all the data is fetched.
*/
_onStoppingRecording: function (endTime) {
this._duration = endTime - this._localStartTime;
this._recording = false;
},
/**
* Sets results available from stopping a recording from SharedPerformanceConnection.
* Should only be called by SharedPerformanceConnection.
*/
_onStopRecording: Task.async(function *(info) {
this._profile = info.profile;
this._duration = info.profilerEndTime - this._profilerStartTime;
this._recording = false;
this._completed = true;
// We filter out all samples that fall out of current profile's range
// since the profiler is continuously running. Because of this, sample
// times are not guaranteed to have a zero epoch, so offset the
// timestamps.
// TODO move this into FakeProfilerFront in ./actors.js after bug 1154115
RecordingUtils.offsetSampleTimes(this._profile, this._profilerStartTime);
// Markers need to be sorted ascending by time, to be properly displayed
@ -248,9 +260,21 @@ RecordingModel.prototype = {
return this._console;
},
/**
* Returns a boolean indicating whether or not this recording model
* has finished recording.
* There is some delay in fetching data between when the recording stops, and
* when the recording is considered completed once it has all the profiler and timeline data.
*/
isCompleted: function () {
return this._completed || this.isImported();
},
/**
* Returns a boolean indicating whether or not this recording model
* is recording.
* A model may no longer be recording, yet still not have the profiler data. In that
* case, use `isCompleted()`.
*/
isRecording: function () {
return this._recording;
@ -262,7 +286,7 @@ RecordingModel.prototype = {
addTimelineData: function (eventName, ...data) {
// If this model isn't currently recording,
// ignore the timeline data.
if (!this._recording) {
if (!this.isRecording()) {
return;
}

View File

@ -63,13 +63,6 @@ const EVENTS = {
// Fired by the PerformanceController when the devtools theme changes.
THEME_CHANGED: "Performance:ThemeChanged",
// When the SharedPerformanceConnection handles profiles created via `console.profile()`,
// the controller handles those events and emits the below events for consumption
// by other views.
CONSOLE_RECORDING_STARTED: "Performance:ConsoleRecordingStarted",
CONSOLE_RECORDING_WILL_STOP: "Performance:ConsoleRecordingWillStop",
CONSOLE_RECORDING_STOPPED: "Performance:ConsoleRecordingStopped",
// Emitted by the PerformanceView when the state (display mode) changes,
// for example when switching between "empty", "recording" or "recorded".
// This causes certain panels to be hidden or visible.
@ -190,9 +183,7 @@ let PerformanceController = {
this._onRecordingSelectFromView = this._onRecordingSelectFromView.bind(this);
this._onPrefChanged = this._onPrefChanged.bind(this);
this._onThemeChanged = this._onThemeChanged.bind(this);
this._onConsoleProfileStart = this._onConsoleProfileStart.bind(this);
this._onConsoleProfileEnd = this._onConsoleProfileEnd.bind(this);
this._onConsoleProfileEnding = this._onConsoleProfileEnding.bind(this);
this._onRecordingStateChange = this._onRecordingStateChange.bind(this);
// All boolean prefs should be handled via the OptionsView in the
// ToolbarView, so that they may be accessible via the "gear" menu.
@ -208,9 +199,10 @@ let PerformanceController = {
this._nonBooleanPrefs.registerObserver();
this._nonBooleanPrefs.on("pref-changed", this._onPrefChanged);
gFront.on("console-profile-start", this._onConsoleProfileStart);
gFront.on("console-profile-ending", this._onConsoleProfileEnding);
gFront.on("console-profile-end", this._onConsoleProfileEnd);
gFront.on("recording-starting", this._onRecordingStateChange);
gFront.on("recording-started", this._onRecordingStateChange);
gFront.on("recording-stopping", this._onRecordingStateChange);
gFront.on("recording-stopped", this._onRecordingStateChange);
ToolbarView.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
PerformanceView.on(EVENTS.UI_START_RECORDING, this.startRecording);
PerformanceView.on(EVENTS.UI_STOP_RECORDING, this.stopRecording);
@ -229,9 +221,10 @@ let PerformanceController = {
this._nonBooleanPrefs.unregisterObserver();
this._nonBooleanPrefs.off("pref-changed", this._onPrefChanged);
gFront.off("console-profile-start", this._onConsoleProfileStart);
gFront.off("console-profile-ending", this._onConsoleProfileEnding);
gFront.off("console-profile-end", this._onConsoleProfileEnd);
gFront.off("recording-starting", this._onRecordingStateChange);
gFront.off("recording-started", this._onRecordingStateChange);
gFront.off("recording-stopping", this._onRecordingStateChange);
gFront.off("recording-stopped", this._onRecordingStateChange);
ToolbarView.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
PerformanceView.off(EVENTS.UI_START_RECORDING, this.startRecording);
PerformanceView.off(EVENTS.UI_STOP_RECORDING, this.stopRecording);
@ -300,12 +293,7 @@ let PerformanceController = {
sampleFrequency: this.getPref("profiler-sample-frequency")
};
this.emit(EVENTS.RECORDING_WILL_START);
let recording = yield gFront.startRecording(options);
this._recordings.push(recording);
this.emit(EVENTS.RECORDING_STARTED, recording);
yield gFront.startRecording(options);
}),
/**
@ -315,9 +303,7 @@ let PerformanceController = {
stopRecording: Task.async(function *() {
let recording = this.getLatestManualRecording();
this.emit(EVENTS.RECORDING_WILL_STOP, recording);
yield gFront.stopRecording(recording);
this.emit(EVENTS.RECORDING_STOPPED, recording);
}),
/**
@ -344,6 +330,11 @@ let PerformanceController = {
if (latest && latest.isRecording()) {
yield this.stopRecording();
}
// If last recording is not recording, but finalizing itself,
// wait for that to finish
if (latest && !latest.isCompleted()) {
yield this.once(EVENTS.RECORDING_STOPPED);
}
this._recordings.length = 0;
this.setCurrentRecording(null);
@ -440,27 +431,33 @@ let PerformanceController = {
},
/**
* Fired when `console.profile()` is executed.
* Fired when a recording model changes state.
*
* @param {string} state
* @param {RecordingModel} model
*/
_onConsoleProfileStart: function (_, recording) {
this._recordings.push(recording);
this.emit(EVENTS.CONSOLE_RECORDING_STARTED, recording);
},
/**
* Fired when `console.profileEnd()` is executed, and the profile
* is stopping soon, as it fetches profiler data.
*/
_onConsoleProfileEnding: function (_, recording) {
this.emit(EVENTS.CONSOLE_RECORDING_WILL_STOP, recording);
},
/**
* Fired when `console.profileEnd()` is executed, and
* has a corresponding `console.profile()` session.
*/
_onConsoleProfileEnd: function (_, recording) {
this.emit(EVENTS.CONSOLE_RECORDING_STOPPED, recording);
_onRecordingStateChange: function (state, model) {
switch (state) {
// Fired when a RecordingModel was just created from the front
case "recording-starting":
// When a recording is just starting, store it internally
this._recordings.push(model);
this.emit(EVENTS.RECORDING_WILL_START, model);
break;
// Fired when a RecordingModel has started recording
case "recording-started":
this.emit(EVENTS.RECORDING_STARTED, model);
break;
// Fired when a RecordingModel is no longer recording, and
// starting to fetch all the profiler data
case "recording-stopping":
this.emit(EVENTS.RECORDING_WILL_STOP, model);
break;
// Fired when a RecordingModel is finished fetching all of its data
case "recording-stopped":
this.emit(EVENTS.RECORDING_STOPPED, model);
break;
}
},
/**
@ -480,21 +477,21 @@ let PerformanceController = {
* model, like `withTicks`, or `withMemory`.
* @option {Array<string>} actors
* An array of strings indicating what actors must exist.
* @option {boolean} isRecording
* A boolean indicating whether the recording must be either recording or not
* recording. Setting to undefined will allow either state.
* @option {boolean} mustBeCompleted
* A boolean indicating whether the recording must be either completed or not.
* Setting to undefined will allow either state.
* @param {RecordingModel} recording
* An optional recording model to use instead of the currently selected.
*
* @return boolean
*/
isFeatureSupported: function ({ features, actors, isRecording: shouldBeRecording }, recording) {
isFeatureSupported: function ({ features, actors, mustBeCompleted }, recording) {
recording = recording || this.getCurrentRecording();
let recordingConfig = recording ? recording.getConfiguration() : {};
let currentRecordingState = recording ? recording.isRecording() : void 0;
let currentCompletedState = recording ? recording.isCompleted() : void 0;
let actorsSupported = gFront.getActorSupport();
if (shouldBeRecording != null && shouldBeRecording !== currentRecordingState) {
if (mustBeCompleted != null && mustBeCompleted !== currentCompletedState) {
return false;
}
if (actors && !actors.every(a => actorsSupported[a])) {

View File

@ -41,12 +41,11 @@ let PerformanceView = {
this._onRecordButtonClick = this._onRecordButtonClick.bind(this);
this._onImportButtonClick = this._onImportButtonClick.bind(this);
this._onClearButtonClick = this._onClearButtonClick.bind(this);
this._lockRecordButton = this._lockRecordButton.bind(this);
this._unlockRecordButton = this._unlockRecordButton.bind(this);
this._lockRecordButtons = this._lockRecordButtons.bind(this);
this._unlockRecordButtons = this._unlockRecordButtons.bind(this);
this._onRecordingSelected = this._onRecordingSelected.bind(this);
this._onRecordingStopped = this._onRecordingStopped.bind(this);
this._onRecordingWillStop = this._onRecordingWillStop.bind(this);
this._onRecordingWillStart = this._onRecordingWillStart.bind(this);
this._onRecordingStarted = this._onRecordingStarted.bind(this);
for (let button of $$(".record-button")) {
button.addEventListener("click", this._onRecordButtonClick);
@ -55,11 +54,8 @@ let PerformanceView = {
this._clearButton.addEventListener("click", this._onClearButtonClick);
// Bind to controller events to unlock the record button
PerformanceController.on(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
PerformanceController.on(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
PerformanceController.on(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
this.setState("empty");
@ -82,11 +78,8 @@ let PerformanceView = {
this._importButton.removeEventListener("click", this._onImportButtonClick);
this._clearButton.removeEventListener("click", this._onClearButtonClick);
PerformanceController.off(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
PerformanceController.off(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
PerformanceController.off(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
yield ToolbarView.destroy();
@ -130,31 +123,30 @@ let PerformanceView = {
* Adds the `locked` attribute on the record button. This prevents it
* from being clicked while recording is started or stopped.
*/
_lockRecordButton: function () {
this._recordButton.setAttribute("locked", "true");
_lockRecordButtons: function () {
for (let button of $$(".record-button")) {
button.setAttribute("locked", "true");
}
},
/**
* Removes the `locked` attribute on the record button.
*/
_unlockRecordButton: function () {
this._recordButton.removeAttribute("locked");
_unlockRecordButtons: function () {
for (let button of $$(".record-button")) {
button.removeAttribute("locked");
}
},
/**
* Fired when a recording is starting, but not yet completed.
* When a recording has started.
*/
_onRecordingWillStart: function () {
this._lockRecordButton();
this._recordButton.setAttribute("checked", "true");
},
/**
* Fired when a recording is stopping, but not yet completed.
*/
_onRecordingWillStop: function () {
this._lockRecordButton();
this._recordButton.removeAttribute("checked");
_onRecordingStarted: function (_, recording) {
// A stopped recording can be from `console.profileEnd` -- only unlock
// the button if it's the main recording that was started via UI.
if (!recording.isConsole()) {
this._unlockRecordButtons();
}
},
/**
@ -164,7 +156,7 @@ let PerformanceView = {
// A stopped recording can be from `console.profileEnd` -- only unlock
// the button if it's the main recording that was started via UI.
if (!recording.isConsole()) {
this._unlockRecordButton();
this._unlockRecordButtons();
}
// If the currently selected recording is the one that just stopped,
@ -187,7 +179,15 @@ let PerformanceView = {
_onRecordButtonClick: function (e) {
if (this._recordButton.hasAttribute("checked")) {
this.emit(EVENTS.UI_STOP_RECORDING);
this._lockRecordButtons();
for (let button of $$(".record-button")) {
button.removeAttribute("checked");
}
} else {
this._lockRecordButtons();
for (let button of $$(".record-button")) {
button.setAttribute("checked", "true");
}
this.emit(EVENTS.UI_START_RECORDING);
}
},

View File

@ -77,7 +77,7 @@
<hbox id="recordings-controls"
class="devtools-toolbarbutton-group">
<toolbarbutton id="main-record-button"
class="devtools-toolbarbutton record-button"
class="devtools-toolbarbutton record-button devtools-thobber"
tooltiptext="&profilerUI.recordButton.tooltip;"/>
<toolbarbutton id="import-button"
class="devtools-toolbarbutton"
@ -159,8 +159,7 @@
flex="1">
<label value="&profilerUI.stopNotice1;"/>
<button class="devtools-toolbarbutton record-button"
standalone="true"
checked="true" />
standalone="true" />
<label value="&profilerUI.stopNotice2;"/>
</hbox>
<hbox id="console-recording-notice"

View File

@ -89,6 +89,7 @@ support-files =
[browser_perf-states.js]
[browser_perf-refresh.js]
[browser_perf-ui-recording.js]
[browser_perf-recording-model-01.js]
[browser_perf-recording-notices-01.js]
[browser_perf-recording-notices-02.js]
[browser_perf_recordings-io-01.js]

View File

@ -15,12 +15,12 @@ function spawnTest () {
yield profilerConnected;
let connection = getPerformanceActorsConnection(target);
let profileStart = once(connection, "console-profile-start");
let profileStart = once(connection, "recording-started");
console.profile("rust");
yield profileStart;
busyWait(WAIT_TIME);
let profileEnd = once(connection, "console-profile-end");
let profileEnd = once(connection, "recording-stopped");
console.profileEnd("rust");
yield profileEnd;

View File

@ -15,10 +15,10 @@ function spawnTest () {
yield profilerConnected;
let connection = getPerformanceActorsConnection(target);
let profileStart = once(connection, "console-profile-start");
let profileStart = once(connection, "recording-started");
console.profile("rust");
yield profileStart;
profileStart = once(connection, "console-profile-start");
profileStart = once(connection, "recording-started");
console.profile("rust2");
yield profileStart;
@ -38,10 +38,10 @@ function spawnTest () {
is(RecordingsView.selectedItem.attachment, recordings[0],
"The first console recording should be selected.");
let profileEnd = once(connection, "console-profile-end");
let profileEnd = once(connection, "recording-stopped");
console.profileEnd("rust");
yield profileEnd;
profileEnd = once(connection, "console-profile-end");
profileEnd = once(connection, "recording-stopped");
console.profileEnd("rust2");
yield profileEnd;

View File

@ -15,15 +15,15 @@ function spawnTest () {
yield profilerConnected;
let connection = getPerformanceActorsConnection(target);
let profileStart = once(connection, "console-profile-start");
let profileStart = once(connection, "recording-started");
console.profile("rust");
yield profileStart;
let profileEnd = once(connection, "console-profile-end");
let profileEnd = once(connection, "recording-stopped");
console.profileEnd("rust");
yield profileEnd;
profileStart = once(connection, "console-profile-start");
profileStart = once(connection, "recording-started");
console.profile("rust2");
yield profileStart;
@ -43,7 +43,7 @@ function spawnTest () {
is(RecordingsView.selectedItem.attachment, recordings[0],
"The first console recording should be selected.");
profileEnd = once(connection, "console-profile-end");
profileEnd = once(connection, "recording-stopped");
console.profileEnd("rust2");
yield profileEnd;

View File

@ -15,14 +15,14 @@ function spawnTest () {
let connection = getPerformanceActorsConnection(target);
let tab = toolbox.doc.getElementById("toolbox-tab-performance");
let profileStart = once(connection, "console-profile-start");
let profileStart = once(connection, "recording-started");
console.profile("rust");
yield profileStart;
ok(tab.hasAttribute("highlighted"),
"performance tab is highlighted during recording from console.profile when unloaded");
let profileEnd = once(connection, "console-profile-end");
let profileEnd = once(connection, "recording-stopped");
console.profileEnd("rust");
yield profileEnd;

View File

@ -0,0 +1,62 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests the state of a recording rec from start to finish for recording,
* completed, and rec data.
*/
function spawnTest () {
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
let { EVENTS, gFront: front, PerformanceController } = panel.panelWin;
let rec = yield front.startRecording({ withMarkers: true, withTicks: true, withMemory: true });
ok(rec.isRecording(), "RecordingModel is recording when created");
yield busyWait(100);
yield waitUntil(() => rec.getMemory().length);
ok(true, "RecordingModel populates memory while recording");
yield waitUntil(() => rec.getTicks().length);
ok(true, "RecordingModel populates ticks while recording");
yield waitUntil(() => rec.getMarkers().length);
ok(true, "RecordingModel populates markers while recording");
ok(!rec.isCompleted(), "RecordingModel is not completed when still recording");
let stopping = once(front, "recording-stopping");
let stopped = once(front, "recording-stopped");
front.stopRecording(rec);
yield stopping;
ok(!rec.isRecording(), "on 'recording-stopping', model is no longer recording");
// This handler should be called BEFORE "recording-stopped" is called, as
// there is some delay, but in the event where "recording-stopped" finishes
// before we check here, ensure that we're atleast in the right state
if (rec.getProfile()) {
ok(rec.isCompleted(), "recording is completed once it has profile data");
} else {
ok(!rec.isCompleted(), "recording is not yet completed on 'recording-stopping'");
}
yield stopped;
ok(!rec.isRecording(), "on 'recording-stopped', model is still no longer recording");
ok(rec.isCompleted(), "on 'recording-stopped', model is considered 'complete'");
// Export and import a rec, and ensure it has the correct state.
let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
let exported = once(PerformanceController, EVENTS.RECORDING_EXPORTED);
yield PerformanceController.exportRecording("", rec, file);
yield exported;
let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
yield PerformanceController.importRecording("", file);
yield imported;
let importedModel = PerformanceController.getCurrentRecording();
ok(importedModel.isCompleted(), "All imported recordings should be completed");
ok(!importedModel.isRecording(), "All imported recordings should not be recording");
yield teardown(panel);
finish();
}

View File

@ -142,7 +142,7 @@ function handleError(aError) {
}
function once(aTarget, aEventName, aUseCapture = false, spread = false) {
info("Waiting for event: '" + aEventName + "' on " + aTarget + ".");
info(`Waiting for event: '${aEventName}' on ${aTarget}`);
let deferred = Promise.defer();
@ -153,6 +153,7 @@ function once(aTarget, aEventName, aUseCapture = false, spread = false) {
]) {
if ((add in aTarget) && (remove in aTarget)) {
aTarget[add](aEventName, function onEvent(...aArgs) {
info(`Received event: '${aEventName}' on ${aTarget}`);
aTarget[remove](aEventName, onEvent, aUseCapture);
deferred.resolve(spread ? aArgs : aArgs[0]);
}, aUseCapture);
@ -305,13 +306,13 @@ function consoleMethod (...args) {
}
function* consoleProfile(win, label) {
let profileStart = once(win.PerformanceController, win.EVENTS.CONSOLE_RECORDING_STARTED);
let profileStart = once(win.PerformanceController, win.EVENTS.RECORDING_STARTED);
consoleMethod("profile", label);
yield profileStart;
}
function* consoleProfileEnd(win, label) {
let ended = once(win.PerformanceController, win.EVENTS.CONSOLE_RECORDING_STOPPED);
let ended = once(win.PerformanceController, win.EVENTS.RECORDING_STOPPED);
consoleMethod("profileEnd", label);
yield ended;
}
@ -475,15 +476,15 @@ function waitUntil(predicate, interval = 10) {
function dragStart(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseDown({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
}
function dragStop(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseUp({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}
function dropSelection(graph) {

View File

@ -17,7 +17,6 @@ let DetailsSubview = {
this._onPrefChanged = this._onPrefChanged.bind(this);
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
PerformanceController.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
@ -32,7 +31,6 @@ let DetailsSubview = {
clearNamedTimeout("range-change-debounce");
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
@ -81,7 +79,7 @@ let DetailsSubview = {
* Called when recording stops or is selected.
*/
_onRecordingStoppedOrSelected: function(_, recording) {
if (!recording || recording.isRecording()) {
if (!recording || !recording.isCompleted()) {
return;
}
if (DetailsView.isViewSelected(this) || this.canUpdateWhileHidden) {
@ -131,7 +129,7 @@ let DetailsSubview = {
// All detail views require a recording to be complete, so do not
// attempt to render if recording is in progress or does not exist.
let recording = PerformanceController.getCurrentRecording();
if (!recording || recording.isRecording()) {
if (!recording || !recording.isCompleted()) {
return;
}

View File

@ -59,7 +59,6 @@ let DetailsView = {
yield this.setAvailableViews();
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
PerformanceController.on(EVENTS.PREF_CHANGED, this.setAvailableViews);
@ -77,7 +76,6 @@ let DetailsView = {
component.initialized && (yield component.view.destroy());
}
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
PerformanceController.off(EVENTS.PREF_CHANGED, this.setAvailableViews);
@ -90,13 +88,13 @@ let DetailsView = {
*/
setAvailableViews: Task.async(function* () {
let recording = PerformanceController.getCurrentRecording();
let isRecording = recording && recording.isRecording();
let isCompleted = recording && recording.isCompleted();
let invalidCurrentView = false;
for (let [name, { view }] of Iterator(this.components)) {
// TODO bug 1160313 get rid of retro mode checks.
let isRetro = PerformanceController.getOption("retro-mode");
let isSupported = isRetro ? name === "js-calltree" : this._isViewSupported(name, false);
let isSupported = isRetro ? name === "js-calltree" : this._isViewSupported(name, true);
// TODO bug 1160313 hide all view buttons, but let js-calltree still be "supported"
$(`toolbarbutton[data-view=${name}]`).hidden = isRetro ? true : !isSupported;
@ -112,12 +110,12 @@ let DetailsView = {
//
// 1: If we currently have selected a view that is no longer valid due
// to feature support, and this isn't the first view, and the current recording
// is not recording.
// is completed.
//
// 2. If we have a finished recording and no panel was selected yet,
// use a default now that we have the recording configurations
if ((this._initialized && !isRecording && invalidCurrentView) ||
(!this._initialized && !isRecording && recording)) {
if ((this._initialized && isCompleted && invalidCurrentView) ||
(!this._initialized && isCompleted && recording)) {
yield this.selectDefaultView();
}
}),
@ -126,12 +124,12 @@ let DetailsView = {
* Takes a view name and optionally if there must be a currently recording in progress.
*
* @param {string} viewName
* @param {boolean?} isRecording
* @param {boolean?} mustBeCompleted
* @return {boolean}
*/
_isViewSupported: function (viewName, isRecording) {
_isViewSupported: function (viewName, mustBeCompleted) {
let { features, actors } = this.components[viewName];
return PerformanceController.isFeatureSupported({ features, actors, isRecording });
return PerformanceController.isFeatureSupported({ features, actors, mustBeCompleted });
},
/**
@ -238,7 +236,7 @@ let DetailsView = {
// All detail views require a recording to be complete, so do not
// attempt to render if recording is in progress or does not exist.
let recording = PerformanceController.getCurrentRecording();
if (recording && !recording.isRecording()) {
if (recording && recording.isCompleted()) {
component.view.shouldUpdateWhenShown = true;
}
}),

View File

@ -66,9 +66,6 @@ let OverviewView = {
PerformanceController.on(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STARTED, this._onRecordingStarted);
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.on(EVENTS.CONSOLE_RECORDING_WILL_STOP, this._onRecordingWillStop);
this.graphs.on("selecting", this._onGraphSelecting);
this.graphs.on("rendered", this._onGraphRendered);
},
@ -84,9 +81,6 @@ let OverviewView = {
PerformanceController.off(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STARTED, this._onRecordingStarted);
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.off(EVENTS.CONSOLE_RECORDING_WILL_STOP, this._onRecordingWillStop);
this.graphs.off("selecting", this._onGraphSelecting);
this.graphs.off("rendered", this._onGraphRendered);
yield this.graphs.destroy();
@ -207,31 +201,25 @@ let OverviewView = {
* exist yet, but can just disable from here. This will only trigger for
* manual recordings.
*/
_onRecordingWillStart: Task.async(function* () {
this._onRecordingStateChange();
_onRecordingWillStart: OverviewViewOnStateChange(Task.async(function* () {
yield this._checkSelection();
this.graphs.dropSelection();
}),
})),
/**
* Called when recording actually starts.
*/
_onRecordingStarted: function (_, recording) {
this._onRecordingStateChange();
},
_onRecordingStarted: OverviewViewOnStateChange(),
/**
* Called when recording will stop.
*/
_onRecordingWillStop: function(_, recording) {
this._onRecordingStateChange();
},
_onRecordingWillStop: OverviewViewOnStateChange(),
/**
* Called when recording actually stops.
*/
_onRecordingStopped: Task.async(function* (_, recording) {
this._onRecordingStateChange();
_onRecordingStopped: OverviewViewOnStateChange(Task.async(function* (_, recording) {
// Check to see if the recording that just stopped is the current recording.
// If it is, render the high-res graphs. For manual recordings, it will also
// be the current recording, but profiles generated by `console.profile` can stop
@ -242,39 +230,21 @@ let OverviewView = {
}
this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
yield this._checkSelection(recording);
}),
})),
/**
* Called when a new recording is selected.
*/
_onRecordingSelected: Task.async(function* (_, recording) {
if (!recording) {
return;
}
this._onRecordingStateChange();
_onRecordingSelected: OverviewViewOnStateChange(Task.async(function* (_, recording) {
this._setGraphVisibilityFromRecordingFeatures(recording);
// If this recording is complete, render the high res graph
if (!recording.isRecording()) {
if (recording.isCompleted()) {
yield this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
}
yield this._checkSelection(recording);
this.graphs.dropSelection();
}),
/**
* Called when a recording is starting, stopping, or about to start/stop.
* Checks the current recording displayed to determine whether or not
* the polling for rendering the overview graph needs to start or stop.
*/
_onRecordingStateChange: function () {
let currentRecording = PerformanceController.getCurrentRecording();
if (!currentRecording || (this.isRendering() && !currentRecording.isRecording())) {
this._stopPolling();
} else if (currentRecording.isRecording() && !this.isRendering()) {
this._startPolling();
}
},
})),
/**
* Start the polling for rendering the overview graph.
@ -303,7 +273,7 @@ let OverviewView = {
* based on whether a recording currently exists and is not in progress.
*/
_checkSelection: Task.async(function* (recording) {
let isEnabled = recording ? !recording.isRecording() : false;
let isEnabled = recording ? recording.isCompleted() : false;
yield this.graphs.selectionEnabled(isEnabled);
}),
@ -375,5 +345,35 @@ let OverviewView = {
toString: () => "[object OverviewView]"
};
/**
* Utility that can wrap a method of OverviewView that
* handles a recording state change like when a recording is starting,
* stopping, or about to start/stop, and determines whether or not
* the polling for rendering the overview graphs needs to start or stop.
* Must be called with the OverviewView context.
*
* @param {function?} fn
* @return {function}
*/
function OverviewViewOnStateChange (fn) {
return function _onRecordingStateChange () {
let currentRecording = PerformanceController.getCurrentRecording();
// All these methods require a recording to exist.
if (!currentRecording) {
return;
}
if (this.isRendering() && !currentRecording.isRecording()) {
this._stopPolling();
} else if (currentRecording.isRecording() && !this.isRendering()) {
this._startPolling();
}
if (fn) {
fn.apply(this, arguments);
}
}
}
// Decorates the OverviewView as an EventEmitter
EventEmitter.decorate(OverviewView);

View File

@ -24,8 +24,6 @@ let RecordingsView = Heritage.extend(WidgetMethods, {
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STARTED, this._onRecordingStarted);
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.on(EVENTS.RECORDING_IMPORTED, this._onRecordingImported);
PerformanceController.on(EVENTS.RECORDINGS_CLEARED, this._onRecordingsCleared);
this.widget.addEventListener("select", this._onSelect, false);
@ -37,8 +35,6 @@ let RecordingsView = Heritage.extend(WidgetMethods, {
destroy: function() {
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STARTED, this._onRecordingStarted);
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.off(EVENTS.RECORDING_IMPORTED, this._onRecordingImported);
PerformanceController.off(EVENTS.RECORDINGS_CLEARED, this._onRecordingsCleared);
this.widget.removeEventListener("select", this._onSelect, false);

View File

@ -76,13 +76,13 @@ registerCleanupFunction(() => {
function dragStart(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseDown({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
}
function dragStop(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseUp({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}

View File

@ -56,6 +56,7 @@ support-files =
[browser_graphs-06.js]
[browser_graphs-07a.js]
[browser_graphs-07b.js]
[browser_graphs-07c.js]
[browser_graphs-08.js]
[browser_graphs-09a.js]
[browser_graphs-09b.js]

View File

@ -91,21 +91,21 @@ function testGraph(graph) {
function hover(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
}
function dragStart(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseDown({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
}
function dragStop(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseUp({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}
let HORIZONTAL_AXIS = 1;
@ -114,8 +114,8 @@ let VERTICAL_AXIS = 2;
function scroll(graph, wheel, axis, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseWheel({ clientX: x, clientY: y, axis, detail: wheel, axis,
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseWheel({ testX: x, testY: y, axis, detail: wheel, axis,
HORIZONTAL_AXIS,
VERTICAL_AXIS
});

View File

@ -68,8 +68,8 @@ let VERTICAL_AXIS = 2;
function scroll(graph, wheel, axis, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseWheel({ clientX: x, clientY: y, axis, detail: wheel, axis,
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseWheel({ testX: x, testY: y, axis, detail: wheel, axis,
HORIZONTAL_AXIS,
VERTICAL_AXIS
});

View File

@ -121,19 +121,19 @@ function testGraph(graph) {
function hover(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
}
function dragStart(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseDown({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
}
function dragStop(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseUp({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}

View File

@ -84,7 +84,7 @@ function map(value, istart, istop, ostart, ostop) {
function click(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseDown({ clientX: x, clientY: y });
graph._onMouseUp({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}

View File

@ -172,29 +172,29 @@ function testGraph(graph, dragStop) {
function hover(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
}
function click(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseDown({ clientX: x, clientY: y });
graph._onMouseUp({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}
function dragStart(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseDown({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
}
function normalDragStop(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseUp({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}
function buggyDragStop(graph, x, y = 1) {
@ -204,13 +204,13 @@ function buggyDragStop(graph, x, y = 1) {
// Only fire a mousemove instead of a mouseup.
// This happens when the mouseup happens outside of the toolbox,
// see Bug 1066504.
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseMove({ clientX: x, clientY: y, buttons: 0 });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseMove({ testX: x, testY: y, buttons: 0 });
}
function scroll(graph, wheel, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseWheel({ clientX: x, clientY: y, detail: wheel });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseWheel({ testX: x, testY: y, detail: wheel });
}

View File

@ -48,19 +48,19 @@ function testGraph(graph) {
function hover(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
}
function dragStart(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseDown({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
}
function dragStop(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseUp({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}

View File

@ -0,0 +1,117 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests if movement via event dispatching using screenX / screenY
// works. All of the other tests directly use the graph's mouse event
// callbacks with textX / testY for convenience.
const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
let {LineGraphWidget} = Cu.import("resource:///modules/devtools/Graphs.jsm", {});
let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
add_task(function*() {
yield promiseTab("about:blank");
yield performTest();
gBrowser.removeCurrentTab();
});
function* performTest() {
let [host, win, doc] = yield createHost();
let graph = new LineGraphWidget(doc.body, "fps");
yield graph.once("ready");
testGraph(graph);
yield graph.destroy();
host.destroy();
}
function testGraph(graph) {
graph.setData(TEST_DATA);
info("Making a selection.");
dragStart(graph, 300);
ok(graph.hasSelectionInProgress(),
"The selection should start (1).");
is(graph.getSelection().start, 300,
"The current selection start value is correct (1).");
is(graph.getSelection().end, 300,
"The current selection end value is correct (1).");
hover(graph, 400);
ok(graph.hasSelectionInProgress(),
"The selection should still be in progress (2).");
is(graph.getSelection().start, 300,
"The current selection start value is correct (2).");
is(graph.getSelection().end, 400,
"The current selection end value is correct (2).");
dragStop(graph, 500);
ok(!graph.hasSelectionInProgress(),
"The selection should have stopped (3).");
is(graph.getSelection().start, 300,
"The current selection start value is correct (3).");
is(graph.getSelection().end, 500,
"The current selection end value is correct (3).");
info("Making a new selection.");
dragStart(graph, 200);
ok(graph.hasSelectionInProgress(),
"The selection should start (4).");
is(graph.getSelection().start, 200,
"The current selection start value is correct (4).");
is(graph.getSelection().end, 200,
"The current selection end value is correct (4).");
hover(graph, 300);
ok(graph.hasSelectionInProgress(),
"The selection should still be in progress (5).");
is(graph.getSelection().start, 200,
"The current selection start value is correct (5).");
is(graph.getSelection().end, 300,
"The current selection end value is correct (5).");
dragStop(graph, 400);
ok(!graph.hasSelectionInProgress(),
"The selection should have stopped (6).");
is(graph.getSelection().start, 200,
"The current selection start value is correct (6).");
is(graph.getSelection().end, 400,
"The current selection end value is correct (6).");
}
// EventUtils just doesn't work!
function dispatchEvent(graph, x, y, type) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
let quad = graph._canvas.getBoxQuads({
relativeTo: window.document
})[0];
let screenX = window.screenX + quad.p1.x + x;
let screenY = window.screenY + quad.p1.y + y;
graph._canvas.dispatchEvent(new MouseEvent(type, {
bubbles: true,
cancelable: true,
buttons: 1,
view: window,
screenX: screenX,
screenY: screenY,
}));
}
function hover(graph, x, y = 1) {
dispatchEvent(graph, x, y, "mousemove");
}
function dragStart(graph, x, y = 1) {
dispatchEvent(graph, x, y, "mousemove");
dispatchEvent(graph, x, y, "mousedown");
}
function dragStop(graph, x, y = 1) {
dispatchEvent(graph, x, y, "mousemove");
dispatchEvent(graph, x, y, "mouseup");
}

View File

@ -46,21 +46,21 @@ function testGraph(graph) {
function click(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseDown({ clientX: x, clientY: y });
graph._onMouseUp({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}
function dragStart(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseDown({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
}
function dragStop(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseUp({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}

View File

@ -121,19 +121,19 @@ function* testGraph(host, graph) {
function hover(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
}
function dragStart(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseDown({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
}
function dragStop(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseUp({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}

View File

@ -135,19 +135,19 @@ function testGraphs(graph1, graph2) {
function hover(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
}
function dragStart(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseDown({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
}
function dragStop(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseUp({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}

View File

@ -70,20 +70,20 @@ function* testGraph(graph) {
function dragStart(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseDown({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
}
function dragStop(graph, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseUp({ clientX: x, clientY: y });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
}
function scroll(graph, wheel, x, y = 1) {
x /= window.devicePixelRatio;
y /= window.devicePixelRatio;
graph._onMouseMove({ clientX: x, clientY: y });
graph._onMouseWheel({ clientX: x, clientY: y, detail: wheel });
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseWheel({ testX: x, testY: y, detail: wheel });
}

View File

@ -731,9 +731,7 @@ FlameGraph.prototype = {
* Listener for the "mousemove" event on the graph's container.
*/
_onMouseMove: function(e) {
let offset = this._getContainerOffset();
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
let mouseY = (e.clientY - offset.top) * this._pixelRatio;
let {mouseX, mouseY} = this._getRelativeEventCoordinates(e);
let canvasWidth = this._width;
let canvasHeight = this._height;
@ -783,9 +781,7 @@ FlameGraph.prototype = {
* Listener for the "mousedown" event on the graph's container.
*/
_onMouseDown: function(e) {
let offset = this._getContainerOffset();
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
let mouseY = (e.clientY - offset.top) * this._pixelRatio;
let {mouseX, mouseY} = this._getRelativeEventCoordinates(e);
this._selectionDragger.origin = mouseX;
this._selectionDragger.anchor.start = this._selection.start;
@ -817,8 +813,7 @@ FlameGraph.prototype = {
* Listener for the "wheel" event on the graph's container.
*/
_onMouseWheel: function(e) {
let offset = this._getContainerOffset();
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
let {mouseX} = this._getRelativeEventCoordinates(e);
let canvasWidth = this._width;
let canvasHeight = this._height;
@ -941,6 +936,27 @@ FlameGraph.prototype = {
return { left: x, top: y };
},
/**
* Given a MouseEvent, make it relative to this._canvas.
* @return object {mouseX,mouseY}
*/
_getRelativeEventCoordinates: function(e) {
// For ease of testing, testX and testY can be passed in as the event
// object.
if ("testX" in e && "testY" in e) {
return {
mouseX: e.testX * this._pixelRatio,
mouseY: e.testY * this._pixelRatio
};
}
let offset = this._getContainerOffset();
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
let mouseY = (e.clientY - offset.top) * this._pixelRatio;
return {mouseX,mouseY};
},
/**
* Listener for the "resize" event on the graph's parent node.
*/

View File

@ -157,6 +157,7 @@ this.AbstractCanvasGraph = function(parent, name, sharpness) {
AbstractCanvasGraph.createIframe(GRAPH_SRC, parent, iframe => {
this._iframe = iframe;
this._window = iframe.contentWindow;
this._topWindow = this._window.top;
this._document = iframe.contentDocument;
this._pixelRatio = sharpness || this._window.devicePixelRatio;
@ -194,7 +195,6 @@ this.AbstractCanvasGraph = function(parent, name, sharpness) {
this._window.addEventListener("mousemove", this._onMouseMove);
this._window.addEventListener("mousedown", this._onMouseDown);
this._window.addEventListener("mouseup", this._onMouseUp);
this._window.addEventListener("MozMousePixelScroll", this._onMouseWheel);
this._window.addEventListener("mouseout", this._onMouseOut);
@ -241,9 +241,10 @@ AbstractCanvasGraph.prototype = {
destroy: Task.async(function *() {
yield this.ready();
this._topWindow.removeEventListener("mousemove", this._onMouseMove);
this._topWindow.removeEventListener("mouseup", this._onMouseUp);
this._window.removeEventListener("mousemove", this._onMouseMove);
this._window.removeEventListener("mousedown", this._onMouseDown);
this._window.removeEventListener("mouseup", this._onMouseUp);
this._window.removeEventListener("MozMousePixelScroll", this._onMouseWheel);
this._window.removeEventListener("mouseout", this._onMouseOut);
@ -939,22 +940,34 @@ AbstractCanvasGraph.prototype = {
},
/**
* Gets the offset of this graph's container relative to the owner window.
*
* @return object
* The { left, top } offset.
* Given a MouseEvent, make it relative to this._canvas.
* @return object {mouseX,mouseY}
*/
_getContainerOffset: function() {
let node = this._canvas;
let x = 0;
let y = 0;
while (node = node.offsetParent) {
x += node.offsetLeft;
y += node.offsetTop;
_getRelativeEventCoordinates: function(e) {
// For ease of testing, testX and testY can be passed in as the event
// object. If so, just return this.
if ("testX" in e && "testY" in e) {
return {
mouseX: e.testX * this._pixelRatio,
mouseY: e.testY * this._pixelRatio
};
}
return { left: x, top: y };
let quad = this._canvas.getBoxQuads({
relativeTo: this._topWindow.document
})[0];
let x = (e.screenX - this._topWindow.screenX) - quad.p1.x;
let y = (e.screenY - this._topWindow.screenY) - quad.p1.y;
// Don't allow the event coordinates to be bigger than the canvas
// or less than 0.
let maxX = quad.p2.x - quad.p1.x;
let maxY = quad.p3.y - quad.p1.y;
let mouseX = Math.max(0, Math.min(x, maxX)) * this._pixelRatio;
let mouseY = Math.max(0, Math.min(x, maxY)) * this._pixelRatio;
return {mouseX,mouseY};
},
/**
@ -964,6 +977,14 @@ AbstractCanvasGraph.prototype = {
let resizer = this._selectionResizer;
let dragger = this._selectionDragger;
// Need to stop propagation here, since this function can be bound
// to both this._window and this._topWindow. It's only attached to
// this._topWindow during a drag event. Null check here since tests
// don't pass this method into the event object.
if (e.stopPropagation && this._isMouseActive) {
e.stopPropagation();
}
// If a mouseup happened outside the toolbox and the current operation
// is causing the selection changed, then end it.
if (e.buttons == 0 && (this.hasSelectionInProgress() ||
@ -972,9 +993,7 @@ AbstractCanvasGraph.prototype = {
return this._onMouseUp(e);
}
let offset = this._getContainerOffset();
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
let mouseY = (e.clientY - offset.top) * this._pixelRatio;
let {mouseX,mouseY} = this._getRelativeEventCoordinates(e);
this._cursor.x = mouseX;
this._cursor.y = mouseY;
@ -1032,8 +1051,7 @@ AbstractCanvasGraph.prototype = {
*/
_onMouseDown: function(e) {
this._isMouseActive = true;
let offset = this._getContainerOffset();
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
let {mouseX} = this._getRelativeEventCoordinates(e);
switch (this._canvas.getAttribute("input")) {
case "hovering-background":
@ -1062,6 +1080,11 @@ AbstractCanvasGraph.prototype = {
break;
}
// During a drag, bind to the top level window so that mouse movement
// outside of this frame will still work.
this._topWindow.addEventListener("mousemove", this._onMouseMove);
this._topWindow.addEventListener("mouseup", this._onMouseUp);
this._shouldRedraw = true;
this.emit("mousedown");
},
@ -1071,8 +1094,7 @@ AbstractCanvasGraph.prototype = {
*/
_onMouseUp: function(e) {
this._isMouseActive = false;
let offset = this._getContainerOffset();
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
let {mouseX} = this._getRelativeEventCoordinates(e);
switch (this._canvas.getAttribute("input")) {
case "hovering-background":
@ -1108,6 +1130,10 @@ AbstractCanvasGraph.prototype = {
break;
}
// No longer dragging, no need to bind to the top level window.
this._topWindow.removeEventListener("mousemove", this._onMouseMove);
this._topWindow.removeEventListener("mouseup", this._onMouseUp);
this._shouldRedraw = true;
this.emit("mouseup");
},
@ -1120,8 +1146,7 @@ AbstractCanvasGraph.prototype = {
return;
}
let offset = this._getContainerOffset();
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
let {mouseX} = this._getRelativeEventCoordinates(e);
let focusX = mouseX;
let selection = this._selection;
@ -1181,7 +1206,7 @@ AbstractCanvasGraph.prototype = {
this.emit("scroll");
},
/**
/**
* Listener for the "mouseout" event on the graph's container.
* Clear any active cursors if a drag isn't happening.
*/

View File

@ -49,6 +49,7 @@ XPCOMUtils.defineLazyGetter(this, "DEFAULT_AREA_PLACEMENTS", function() {
"urlbar-container",
"search-container",
"bookmarks-menu-button",
"pocket-button",
"downloads-button",
"home-button",
"social-share-button",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1953,6 +1953,9 @@ public class BrowserApp extends GeckoApp
});
}
} else {
if (mDoorHangerPopup != null) {
mDoorHangerPopup.disable();
}
mTabsPanel.show(panel);
}
}
@ -1960,6 +1963,9 @@ public class BrowserApp extends GeckoApp
@Override
public void hideTabs() {
mTabsPanel.hide();
if (mDoorHangerPopup != null) {
mDoorHangerPopup.enable();
}
}
@Override
@ -2234,6 +2240,14 @@ public class BrowserApp extends GeckoApp
// Expected to be fixed by bug 915825.
hideHomePager(url);
loadUrlOrKeywordSearch(url);
clearSelectedTabApplicationId();
}
private void clearSelectedTabApplicationId() {
final Tab selected = Tabs.getInstance().getSelectedTab();
if (selected != null) {
selected.setApplicationId(null);
}
}
private void loadUrlOrKeywordSearch(final String url) {
@ -3572,6 +3586,7 @@ public class BrowserApp extends GeckoApp
startActivity(intent);
} else if (!maybeSwitchToTab(url, flags)) {
openUrlAndStopEditing(url);
clearSelectedTabApplicationId();
}
}

View File

@ -19,9 +19,8 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.provider.Browser;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -1424,27 +1423,33 @@ public abstract class GeckoApp
}
/**
* Loads the initial tab at Fennec startup.
*
* If Fennec was opened with an external URL, that URL will be loaded.
* Otherwise, unless there was a session restore, the default URL
* (about:home) be loaded.
*
* @param url External URL to load, or null to load the default URL
* Loads the initial tab at Fennec startup. If we don't restore tabs, this
* tab will be about:home. If we restore tabs, we don't need to create a new tab.
*/
protected void loadStartupTab(String url, int flags) {
if (url == null) {
if (!mShouldRestore) {
// Show about:home if we aren't restoring previous session and
// there's no external URL.
Tabs.getInstance().loadUrl(AboutPages.HOME, flags);
}
} else {
// If given an external URL, load it
Tabs.getInstance().loadUrl(url, flags);
protected void loadStartupTabWithAboutHome(final int flags) {
if (!mShouldRestore) {
Tabs.getInstance().loadUrl(AboutPages.HOME, flags);
}
}
/**
* Loads the initial tab at Fennec startup. This tab will load with the given
* external URL. If that URL is invalid, about:home will be loaded.
*
* @param url External URL to load.
* @param extraApplicationId Identifies the calling application; delivered with the URL
*/
protected void loadStartupTabWithExternalUrl(final String url, final String extraApplicationId,
final int flags) {
// Invalid url
if (url == null) {
loadStartupTabWithAboutHome(flags);
return;
}
Tabs.getInstance().loadUrl(url, extraApplicationId, flags);
}
private void initialize() {
mInitialized = true;
@ -1509,10 +1514,11 @@ public abstract class GeckoApp
if (ACTION_HOMESCREEN_SHORTCUT.equals(action)) {
flags |= Tabs.LOADURL_PINNED;
}
loadStartupTab(passedUri, flags);
final String extraApplicationId = intent.getStringExtra(Browser.EXTRA_APPLICATION_ID);
loadStartupTabWithExternalUrl(passedUri, extraApplicationId, flags);
} else {
if (!mIsRestoringActivity) {
loadStartupTab(null, Tabs.LOADURL_NEW_TAB);
loadStartupTabWithAboutHome(Tabs.LOADURL_NEW_TAB);
}
Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED);
@ -1824,9 +1830,10 @@ public abstract class GeckoApp
TabQueueHelper.openQueuedUrls(GeckoApp.this, mProfile, TabQueueHelper.FILE_NAME, true);
} else {
String uri = intent.getDataString();
Tabs.getInstance().loadUrl(uri, Tabs.LOADURL_NEW_TAB |
Tabs.LOADURL_USER_ENTERED |
Tabs.LOADURL_EXTERNAL);
final String extraApplicationId = intent.getStringExtra(Browser.EXTRA_APPLICATION_ID);
Tabs.getInstance().loadUrl(uri, extraApplicationId, Tabs.LOADURL_NEW_TAB |
Tabs.LOADURL_USER_ENTERED |
Tabs.LOADURL_EXTERNAL);
}
}
});

View File

@ -50,6 +50,7 @@ public class Tab {
private String mTitle;
private Bitmap mFavicon;
private String mFaviconUrl;
private String mApplicationId; // Intended to be null after explicit user action.
// The set of all available Favicons for this tab, sorted by attractiveness.
final TreeSet<RemoteFavicon> mAvailableFavicons = new TreeSet<>();
@ -194,6 +195,14 @@ public class Tab {
return mFavicon;
}
protected String getApplicationId() {
return mApplicationId;
}
protected void setApplicationId(final String applicationId) {
mApplicationId = applicationId;
}
public BitmapDrawable getThumbnail() {
return mThumbnail;
}

Some files were not shown because too many files have changed in this diff Show More