mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to b2g-inbound.
This commit is contained in:
commit
f7fa3b9a38
@ -3886,8 +3886,7 @@ var XULBrowserWindow = {
|
||||
if (this.hideChromeForLocation(location)) {
|
||||
document.documentElement.setAttribute("disablechrome", "true");
|
||||
} else {
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
if (ss.getTabValue(gBrowser.selectedTab, "appOrigin"))
|
||||
if (SessionStore.getTabValue(gBrowser.selectedTab, "appOrigin"))
|
||||
document.documentElement.setAttribute("disablechrome", "true");
|
||||
else
|
||||
document.documentElement.removeAttribute("disablechrome");
|
||||
@ -6231,7 +6230,7 @@ function convertFromUnicode(charset, str)
|
||||
/**
|
||||
* Re-open a closed tab.
|
||||
* @param aIndex
|
||||
* The index of the tab (via nsSessionStore.getClosedTabData)
|
||||
* The index of the tab (via SessionStore.getClosedTabData)
|
||||
* @returns a reference to the reopened tab.
|
||||
*/
|
||||
function undoCloseTab(aIndex) {
|
||||
@ -6240,17 +6239,15 @@ function undoCloseTab(aIndex) {
|
||||
if (gBrowser.tabs.length == 1 && isTabEmpty(gBrowser.selectedTab))
|
||||
blankTabToRemove = gBrowser.selectedTab;
|
||||
|
||||
var ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
let numberOfTabsToUndoClose = 0;
|
||||
let index = Number(aIndex);
|
||||
|
||||
|
||||
if (isNaN(index)) {
|
||||
index = 0;
|
||||
numberOfTabsToUndoClose = ss.getNumberOfTabsClosedLast(window);
|
||||
numberOfTabsToUndoClose = SessionStore.getNumberOfTabsClosedLast(window);
|
||||
} else {
|
||||
if (0 > index || index >= ss.getClosedTabCount(window))
|
||||
if (0 > index || index >= SessionStore.getClosedTabCount(window))
|
||||
return null;
|
||||
numberOfTabsToUndoClose = 1;
|
||||
}
|
||||
@ -6259,7 +6256,7 @@ function undoCloseTab(aIndex) {
|
||||
while (numberOfTabsToUndoClose > 0 &&
|
||||
numberOfTabsToUndoClose--) {
|
||||
TabView.prepareUndoCloseTab(blankTabToRemove);
|
||||
tab = ss.undoCloseTab(window, index);
|
||||
tab = SessionStore.undoCloseTab(window, index);
|
||||
TabView.afterUndoCloseTab();
|
||||
if (blankTabToRemove) {
|
||||
gBrowser.removeTab(blankTabToRemove);
|
||||
@ -6268,22 +6265,20 @@ function undoCloseTab(aIndex) {
|
||||
}
|
||||
|
||||
// Reset the number of tabs closed last time to the default.
|
||||
ss.setNumberOfTabsClosedLast(window, 1);
|
||||
SessionStore.setNumberOfTabsClosedLast(window, 1);
|
||||
return tab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-open a closed window.
|
||||
* @param aIndex
|
||||
* The index of the window (via nsSessionStore.getClosedWindowData)
|
||||
* The index of the window (via SessionStore.getClosedWindowData)
|
||||
* @returns a reference to the reopened window.
|
||||
*/
|
||||
function undoCloseWindow(aIndex) {
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
let window = null;
|
||||
if (ss.getClosedWindowCount() > (aIndex || 0))
|
||||
window = ss.undoCloseWindow(aIndex || 0);
|
||||
if (SessionStore.getClosedWindowCount() > (aIndex || 0))
|
||||
window = SessionStore.undoCloseWindow(aIndex || 0);
|
||||
|
||||
return window;
|
||||
}
|
||||
@ -7036,9 +7031,7 @@ function switchToTabHavingURI(aURI, aOpenNew) {
|
||||
}
|
||||
|
||||
function restoreLastSession() {
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
ss.restoreLastSession();
|
||||
SessionStore.restoreLastSession();
|
||||
}
|
||||
|
||||
var TabContextMenu = {
|
||||
@ -7062,10 +7055,8 @@ var TabContextMenu = {
|
||||
menuItem.disabled = disabled;
|
||||
|
||||
// Session store
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
let undoCloseTabElement = document.getElementById("context_undoCloseTab");
|
||||
let closedTabCount = ss.getNumberOfTabsClosedLast(window);
|
||||
let closedTabCount = SessionStore.getNumberOfTabsClosedLast(window);
|
||||
undoCloseTabElement.disabled = closedTabCount == 0;
|
||||
// Change the label of "Undo Close Tab" to specify if it will undo a batch-close
|
||||
// or a single close.
|
||||
@ -7149,9 +7140,7 @@ function safeModeRestart()
|
||||
* delta is the offset to the history entry that you want to load.
|
||||
*/
|
||||
function duplicateTabIn(aTab, where, delta) {
|
||||
let newTab = Cc['@mozilla.org/browser/sessionstore;1']
|
||||
.getService(Ci.nsISessionStore)
|
||||
.duplicateTab(window, aTab, delta);
|
||||
let newTab = SessionStore.duplicateTab(window, aTab, delta);
|
||||
|
||||
switch (where) {
|
||||
case "window":
|
||||
|
@ -1705,9 +1705,7 @@
|
||||
for (let i = numberOfTabsToClose - 1; i >= 0; --i) {
|
||||
this.removeTab(tabs[i], {animate: true});
|
||||
}
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
ss.setNumberOfTabsClosedLast(window, numberOfTabsToClose);
|
||||
SessionStore.setNumberOfTabsClosedLast(window, numberOfTabsToClose);
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
@ -1731,9 +1729,7 @@
|
||||
closedTabs++;
|
||||
}
|
||||
}
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
ss.setNumberOfTabsClosedLast(window, closedTabs);
|
||||
SessionStore.setNumberOfTabsClosedLast(window, closedTabs);
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
@ -1762,9 +1758,7 @@
|
||||
var byMouse = aParams.byMouse;
|
||||
}
|
||||
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
ss.setNumberOfTabsClosedLast(window, 1);
|
||||
SessionStore.setNumberOfTabsClosedLast(window, 1);
|
||||
|
||||
// Handle requests for synchronously removing an already
|
||||
// asynchronously closing tab.
|
||||
@ -2148,9 +2142,7 @@
|
||||
// then do not switch docShells but retrieve the other tab's state
|
||||
// and apply it to our tab.
|
||||
if (isPending) {
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"]
|
||||
.getService(Ci.nsISessionStore)
|
||||
ss.setTabState(aOurTab, ss.getTabState(aOtherTab));
|
||||
SessionStore.setTabState(aOurTab, SessionStore.getTabState(aOtherTab));
|
||||
|
||||
// Make sure to unregister any open URIs.
|
||||
this._swapRegisteredOpenURIs(ourBrowser, otherBrowser);
|
||||
@ -2575,9 +2567,7 @@
|
||||
<parameter name="aTab"/><!-- can be from a different window as well -->
|
||||
<body>
|
||||
<![CDATA[
|
||||
return Cc["@mozilla.org/browser/sessionstore;1"]
|
||||
.getService(Ci.nsISessionStore)
|
||||
.duplicateTab(window, aTab);
|
||||
return SessionStore.duplicateTab(window, aTab);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -13,7 +13,6 @@ include $(DEPTH)/config/autoconf.mk
|
||||
MOCHITEST_BROWSER_FILES = \
|
||||
head.js \
|
||||
blocklist.xml \
|
||||
blocklistEmpty.xml \
|
||||
browser_blocklist.js \
|
||||
browser_defaults.js \
|
||||
browser_addons.js \
|
||||
|
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
|
||||
<emItems>
|
||||
<emItem blockID="s2" id="nothing@services.mozilla.org"></emItem>
|
||||
</emItems>
|
||||
</blocklist>
|
@ -302,9 +302,10 @@ var tests = {
|
||||
Services.prefs.clearUserPref("social.whitelist");
|
||||
let provider = Social._getProviderFromOrigin(addonManifest.origin);
|
||||
is(provider.manifest.version, 2, "manifest version is 2");
|
||||
Social.uninstallProvider(addonManifest.origin);
|
||||
gBrowser.removeTab(tab);
|
||||
next();
|
||||
Social.uninstallProvider(addonManifest.origin, function() {
|
||||
gBrowser.removeTab(tab);
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
let port = provider.getWorkerPort();
|
||||
|
@ -8,7 +8,6 @@ let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).So
|
||||
|
||||
const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
|
||||
let blocklistURL = "http://test:80/browser/browser/base/content/test/social/blocklist.xml";
|
||||
let blocklistEmpty = "http://test:80/browser/browser/base/content/test/social/blocklistEmpty.xml";
|
||||
|
||||
let manifest = { // normal provider
|
||||
name: "provider ok",
|
||||
@ -39,7 +38,7 @@ var tests = {
|
||||
setAndUpdateBlocklist(blocklistURL, function() {
|
||||
ok(Services.blocklist.isAddonBlocklisted("test1.example.com@services.mozilla.org", "0", "0", "0"), "blocking 'blocked'");
|
||||
ok(!Services.blocklist.isAddonBlocklisted("example.com@services.mozilla.org", "0", "0", "0"), "not blocking 'good'");
|
||||
setAndUpdateBlocklist(blocklistEmpty, function() {
|
||||
resetBlocklist(function() {
|
||||
ok(!Services.blocklist.isAddonBlocklisted("test1.example.com@services.mozilla.org", "0", "0", "0"), "blocklist cleared");
|
||||
next();
|
||||
});
|
||||
@ -49,7 +48,7 @@ var tests = {
|
||||
function finish(isgood) {
|
||||
ok(isgood, "adding non-blocked provider ok");
|
||||
Services.prefs.clearUserPref("social.manifest.good");
|
||||
setAndUpdateBlocklist(blocklistEmpty, next);
|
||||
resetBlocklist(next);
|
||||
}
|
||||
setManifestPref("social.manifest.good", manifest);
|
||||
setAndUpdateBlocklist(blocklistURL, function() {
|
||||
@ -75,7 +74,7 @@ var tests = {
|
||||
function finish(good) {
|
||||
ok(good, "Unable to add blocklisted provider");
|
||||
Services.prefs.clearUserPref("social.manifest.blocked");
|
||||
setAndUpdateBlocklist(blocklistEmpty, next);
|
||||
resetBlocklist(next);
|
||||
}
|
||||
setManifestPref("social.manifest.blocked", manifest_bad);
|
||||
setAndUpdateBlocklist(blocklistURL, function() {
|
||||
@ -85,7 +84,7 @@ var tests = {
|
||||
finish(false);
|
||||
});
|
||||
} catch(e) {
|
||||
ok(true, "SocialService.addProvider should throw blocklist exception");
|
||||
ok(true, "SocialService.addProvider should throw blocklist exception: " + e);
|
||||
finish(true);
|
||||
}
|
||||
});
|
||||
@ -94,7 +93,7 @@ var tests = {
|
||||
function finish(good) {
|
||||
ok(good, "Unable to add blocklisted provider");
|
||||
Services.prefs.clearUserPref("social.whitelist");
|
||||
setAndUpdateBlocklist(blocklistEmpty, next);
|
||||
resetBlocklist(next);
|
||||
}
|
||||
let activationURL = manifest_bad.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||
addTab(activationURL, function(tab) {
|
||||
@ -119,29 +118,62 @@ var tests = {
|
||||
});
|
||||
},
|
||||
testBlockingExistingProvider: function(next) {
|
||||
let windowWasClosed = false;
|
||||
function finish() {
|
||||
waitForCondition(function() windowWasClosed, function() {
|
||||
Services.wm.removeListener(listener);
|
||||
next();
|
||||
}, "blocklist dialog was closed");
|
||||
}
|
||||
|
||||
addWindowListener(URI_EXTENSION_BLOCKLIST_DIALOG, function(win) {
|
||||
win.close();
|
||||
ok(true, "window closed");
|
||||
});
|
||||
let listener = {
|
||||
_window: null,
|
||||
onOpenWindow: function(aXULWindow) {
|
||||
Services.wm.removeListener(this);
|
||||
this._window = aXULWindow;
|
||||
let domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
|
||||
domwindow.addEventListener("unload", function _unload() {
|
||||
domwindow.removeEventListener("unload", _unload, false);
|
||||
windowWasClosed = true;
|
||||
}, false);
|
||||
info("dialog opened, waiting for focus");
|
||||
waitForFocus(function() {
|
||||
is(domwindow.document.location.href, URI_EXTENSION_BLOCKLIST_DIALOG, "dialog opened and focused");
|
||||
executeSoon(function() {
|
||||
domwindow.close();
|
||||
});
|
||||
}, domwindow);
|
||||
},
|
||||
onCloseWindow: function(aXULWindow) { },
|
||||
onWindowTitleChange: function(aXULWindow, aNewTitle) { }
|
||||
};
|
||||
|
||||
Services.wm.addListener(listener);
|
||||
|
||||
setManifestPref("social.manifest.blocked", manifest_bad);
|
||||
SocialService.addProvider(manifest_bad, function(provider) {
|
||||
if (provider) {
|
||||
try {
|
||||
SocialService.addProvider(manifest_bad, function(provider) {
|
||||
// the act of blocking should cause a 'provider-removed' notification
|
||||
// from SocialService.
|
||||
SocialService.registerProviderListener(function providerListener() {
|
||||
SocialService.registerProviderListener(function providerListener(topic) {
|
||||
if (topic != "provider-removed")
|
||||
return;
|
||||
SocialService.unregisterProviderListener(providerListener);
|
||||
SocialService.getProvider(provider.origin, function(p) {
|
||||
ok(p==null, "blocklisted provider removed");
|
||||
Services.prefs.clearUserPref("social.manifest.blocked");
|
||||
setAndUpdateBlocklist(blocklistEmpty, next);
|
||||
resetBlocklist(finish);
|
||||
});
|
||||
});
|
||||
// no callback - the act of updating should cause the listener above
|
||||
// to fire.
|
||||
setAndUpdateBlocklist(blocklistURL);
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch(e) {
|
||||
ok(false, "unable to add provider " + e);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +165,8 @@ function runSocialTests(tests, cbPreTest, cbPostTest, cbFinish) {
|
||||
} catch (err if err instanceof StopIteration) {
|
||||
// out of items:
|
||||
(cbFinish || defaultFinishChecks)();
|
||||
info("runSocialTests: finish test run with " + Social.providers.length + " providers");
|
||||
is(providersAtStart, Social.providers.length,
|
||||
"runSocialTests: finish test run with " + Social.providers.length + " providers");
|
||||
return;
|
||||
}
|
||||
// We run on a timeout as the frameworker also makes use of timeouts, so
|
||||
@ -314,26 +315,6 @@ function resetBuiltinManifestPref(name) {
|
||||
Services.prefs.PREF_INVALID, "default manifest removed");
|
||||
}
|
||||
|
||||
function addWindowListener(aURL, aCallback) {
|
||||
Services.wm.addListener({
|
||||
onOpenWindow: function(aXULWindow) {
|
||||
info("window opened, waiting for focus");
|
||||
Services.wm.removeListener(this);
|
||||
|
||||
var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
waitForFocus(function() {
|
||||
is(domwindow.document.location.href, aURL, "window opened and focused");
|
||||
executeSoon(function() {
|
||||
aCallback(domwindow);
|
||||
});
|
||||
}, domwindow);
|
||||
},
|
||||
onCloseWindow: function(aXULWindow) { },
|
||||
onWindowTitleChange: function(aXULWindow, aNewTitle) { }
|
||||
});
|
||||
}
|
||||
|
||||
function addTab(url, callback) {
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true});
|
||||
tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
|
||||
|
@ -25,7 +25,7 @@ interface nsIDOMNode;
|
||||
* |gBrowser.tabContainer| such as e.g. |gBrowser.selectedTab|.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(700756cc-f5c7-11e2-b842-59d9dc830245)]
|
||||
[scriptable, uuid(7ffd8bfa-0a8f-11e3-b1c0-1e3faed8a8ba)]
|
||||
interface nsISessionStore : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -208,6 +208,25 @@ interface nsISessionStore : nsISupports
|
||||
*/
|
||||
void deleteTabValue(in nsIDOMNode aTab, in AString aKey);
|
||||
|
||||
/**
|
||||
* @param aKey is the value's name.
|
||||
*
|
||||
* @returns A string value or an empty string if none is set.
|
||||
*/
|
||||
AString getGlobalValue(in AString aKey);
|
||||
|
||||
/**
|
||||
* @param aKey is the value's name.
|
||||
* @param aStringValue is the value itself (use JSON.stringify/parse before setting JS objects).
|
||||
*/
|
||||
void setGlobalValue(in AString aKey, in AString aStringValue);
|
||||
|
||||
/**
|
||||
* @param aTab is the browser tab to get the value for.
|
||||
* @param aKey is the value's name.
|
||||
*/
|
||||
void deleteGlobalValue(in AString aKey);
|
||||
|
||||
/**
|
||||
* @param aName is the name of the attribute to save/restore for all tabbrowser tabs.
|
||||
*/
|
||||
|
@ -256,6 +256,18 @@ this.SessionStore = {
|
||||
SessionStoreInternal.deleteTabValue(aTab, aKey);
|
||||
},
|
||||
|
||||
getGlobalValue: function ss_getGlobalValue(aKey) {
|
||||
return SessionStoreInternal.getGlobalValue(aKey);
|
||||
},
|
||||
|
||||
setGlobalValue: function ss_setGlobalValue(aKey, aStringValue) {
|
||||
SessionStoreInternal.setGlobalValue(aKey, aStringValue);
|
||||
},
|
||||
|
||||
deleteGlobalValue: function ss_deleteGlobalValue(aKey) {
|
||||
SessionStoreInternal.deleteGlobalValue(aKey);
|
||||
},
|
||||
|
||||
persistTabAttribute: function ss_persistTabAttribute(aName) {
|
||||
SessionStoreInternal.persistTabAttribute(aName);
|
||||
},
|
||||
@ -314,6 +326,9 @@ let SessionStoreInternal = {
|
||||
// states for all recently closed windows
|
||||
_closedWindows: [],
|
||||
|
||||
// state saved globally for a session
|
||||
_globalValues: {},
|
||||
|
||||
// collection of session states yet to be restored
|
||||
_statesToRestore: {},
|
||||
|
||||
@ -1701,6 +1716,20 @@ let SessionStoreInternal = {
|
||||
this.saveStateDelayed(aTab.ownerDocument.defaultView);
|
||||
},
|
||||
|
||||
getGlobalValue: function ssi_getGlobalValue(aKey) {
|
||||
return this._globalValues[aKey] || "";
|
||||
},
|
||||
|
||||
setGlobalValue: function ssi_setGlobalValue(aKey, aStringValue) {
|
||||
this._globalValues[aKey] = aStringValue;
|
||||
this.saveStateDelayed();
|
||||
},
|
||||
|
||||
deleteGlobalValue: function ssi_deleteGlobalValue(aKey) {
|
||||
delete this._globalValues[aKey];
|
||||
this.saveStateDelayed();
|
||||
},
|
||||
|
||||
persistTabAttribute: function ssi_persistTabAttribute(aName) {
|
||||
if (TabAttributes.persist(aName)) {
|
||||
TabStateCache.clear();
|
||||
@ -1795,9 +1824,8 @@ let SessionStoreInternal = {
|
||||
this._capClosedWindows();
|
||||
}
|
||||
|
||||
if (lastSessionState.scratchpads) {
|
||||
ScratchpadManager.restoreSession(lastSessionState.scratchpads);
|
||||
}
|
||||
this._setGlobalValuesFromState(aState);
|
||||
this._restoreScratchPads();
|
||||
|
||||
// Set data that persists between sessions
|
||||
this._recentCrashes = lastSessionState.session &&
|
||||
@ -1809,6 +1837,26 @@ let SessionStoreInternal = {
|
||||
this._lastSessionState = null;
|
||||
},
|
||||
|
||||
_setGlobalValuesFromState: function ssi_setGlobalValuesFromState(aState) {
|
||||
if (aState && aState.global) {
|
||||
this._globalValues = aState.global;
|
||||
}
|
||||
},
|
||||
|
||||
_restoreScratchPads: function ssi_restoreScratchPads() {
|
||||
let scratchpads;
|
||||
try {
|
||||
scratchpads = JSON.parse(this.getGlobalValue('scratchpads'));
|
||||
} catch (ex) {
|
||||
// Ignore any errors when attempting to decode the scratchpads.
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
|
||||
if (scratchpads) {
|
||||
ScratchpadManager.restoreSession(scratchpads);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* See if aWindow is usable for use when restoring a previous session via
|
||||
* restoreLastSession. If usable, prepare it for use.
|
||||
@ -2439,14 +2487,14 @@ let SessionStoreInternal = {
|
||||
};
|
||||
|
||||
// get open Scratchpad window states too
|
||||
var scratchpads = ScratchpadManager.getSessionState();
|
||||
this.setGlobalValue('scratchpads', JSON.stringify(ScratchpadManager.getSessionState()));
|
||||
|
||||
let state = {
|
||||
windows: total,
|
||||
selectedWindow: ix + 1,
|
||||
_closedWindows: lastClosedWindowsCopy,
|
||||
session: session,
|
||||
scratchpads: scratchpads
|
||||
global: this._globalValues
|
||||
};
|
||||
|
||||
// Persist the last session if we deferred restoring it
|
||||
@ -2690,9 +2738,8 @@ let SessionStoreInternal = {
|
||||
this.restoreHistoryPrecursor(aWindow, tabs, winData.tabs,
|
||||
(overwriteTabs ? (parseInt(winData.selected) || 1) : 0), 0, 0);
|
||||
|
||||
if (aState.scratchpads) {
|
||||
ScratchpadManager.restoreSession(aState.scratchpads);
|
||||
}
|
||||
this._setGlobalValuesFromState(aState);
|
||||
this._restoreScratchPads();
|
||||
|
||||
// set smoothScroll back to the original value
|
||||
tabstrip.smoothScroll = smoothScroll;
|
||||
|
@ -24,6 +24,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
browser_form_restore_events_sample.html \
|
||||
browser_formdata_format.js \
|
||||
browser_formdata_format_sample.html \
|
||||
browser_global_store.js \
|
||||
browser_input.js \
|
||||
browser_input_sample.html \
|
||||
browser_pageshow.js \
|
||||
|
@ -1,16 +1,20 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const scratchpads = [
|
||||
{ text: "text1", executionContext: 1 },
|
||||
{ text: "", executionContext: 2, filename: "test.js" }
|
||||
];
|
||||
|
||||
const testState = {
|
||||
windows: [{
|
||||
tabs: [
|
||||
{ entries: [{ url: "about:blank" }] },
|
||||
]
|
||||
}],
|
||||
scratchpads: [
|
||||
{ text: "text1", executionContext: 1 },
|
||||
{ text: "", executionContext: 2, filename: "test.js" }
|
||||
]
|
||||
global: {
|
||||
scratchpads: JSON.stringify(scratchpads)
|
||||
}
|
||||
};
|
||||
|
||||
// only finish() when correct number of windows opened
|
||||
@ -18,8 +22,8 @@ var restored = [];
|
||||
function addState(state) {
|
||||
restored.push(state);
|
||||
|
||||
if (restored.length == testState.scratchpads.length) {
|
||||
ok(statesMatch(restored, testState.scratchpads),
|
||||
if (restored.length == scratchpads.length) {
|
||||
ok(statesMatch(restored, scratchpads),
|
||||
"Two scratchpad windows restored");
|
||||
|
||||
Services.ww.unregisterNotification(windowObserver);
|
||||
|
49
browser/components/sessionstore/test/browser_global_store.js
Normal file
49
browser/components/sessionstore/test/browser_global_store.js
Normal file
@ -0,0 +1,49 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests the API for saving global session data.
|
||||
function runTests() {
|
||||
const key1 = "Unique name 1: " + Date.now();
|
||||
const key2 = "Unique name 2: " + Date.now();
|
||||
const value1 = "Unique value 1: " + Math.random();
|
||||
const value2 = "Unique value 2: " + Math.random();
|
||||
|
||||
let global = {};
|
||||
global[key1] = value1;
|
||||
|
||||
const testState = {
|
||||
windows: [
|
||||
{
|
||||
tabs: [
|
||||
{ entries: [{ url: "about:blank" }] },
|
||||
]
|
||||
}
|
||||
],
|
||||
global: global
|
||||
};
|
||||
|
||||
function testRestoredState() {
|
||||
is(ss.getGlobalValue(key1), value1, "restored state has global value");
|
||||
}
|
||||
|
||||
function testGlobalStore() {
|
||||
is(ss.getGlobalValue(key2), "", "global value initially not set");
|
||||
|
||||
ss.setGlobalValue(key2, value1);
|
||||
is(ss.getGlobalValue(key2), value1, "retreived value matches stored");
|
||||
|
||||
ss.setGlobalValue(key2, value2);
|
||||
is(ss.getGlobalValue(key2), value2, "previously stored value was overwritten");
|
||||
|
||||
ss.deleteGlobalValue(key2);
|
||||
is(ss.getGlobalValue(key2), "", "global value was deleted");
|
||||
}
|
||||
|
||||
yield waitForBrowserState(testState, next);
|
||||
testRestoredState();
|
||||
testGlobalStore();
|
||||
}
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
@ -362,6 +362,17 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
this.selectedItem = newItem;
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens selected item in a new tab.
|
||||
*/
|
||||
openRequestInTab: function() {
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
|
||||
let selected = this.selectedItem.attachment;
|
||||
|
||||
win.openUILinkIn(selected.url, "tab", { relatedToCurrent: true });
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy the request url from the currently selected item.
|
||||
*/
|
||||
|
@ -20,6 +20,10 @@
|
||||
<popupset id="networkPopupSet">
|
||||
<menupopup id="network-request-popup"
|
||||
onpopupshowing="NetMonitorView.RequestsMenu._onContextShowing(event);">
|
||||
<menuitem id="request-menu-context-newtab"
|
||||
label="&netmonitorUI.context.newTab;"
|
||||
accesskey="&netmonitorUI.context.newTab.accesskey;"
|
||||
oncommand="NetMonitorView.RequestsMenu.openRequestInTab();"/>
|
||||
<menuitem id="request-menu-context-copy-url"
|
||||
label="&netmonitorUI.context.copyUrl;"
|
||||
accesskey="&netmonitorUI.context.copyUrl.accesskey;"
|
||||
|
@ -45,6 +45,7 @@ MOCHITEST_BROWSER_TESTS = \
|
||||
browser_net_resend.js \
|
||||
browser_net_req-resp-bodies.js \
|
||||
browser_net_copy_url.js \
|
||||
browser_net_open_request_in_tab.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
|
@ -0,0 +1,40 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if Open in new tab works.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
initNetMonitor(CUSTOM_GET_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test...");
|
||||
|
||||
let { NetMonitorView } = aMonitor.panelWin;
|
||||
let { RequestsMenu } = NetMonitorView;
|
||||
|
||||
RequestsMenu.lazyUpdate = false;
|
||||
|
||||
waitForNetworkEvents(aMonitor, 1).then(() => {
|
||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||
RequestsMenu.selectedItem = requestItem;
|
||||
|
||||
gBrowser.tabContainer.addEventListener("TabOpen",function onOpen(event){
|
||||
ok(true, "A new tab has been opened ");
|
||||
gBrowser.tabContainer.removeEventListener("TabOpen", onOpen, false);
|
||||
cleanUp();
|
||||
}, false);
|
||||
|
||||
RequestsMenu.openRequestInTab();
|
||||
});
|
||||
|
||||
aDebuggee.performRequests(1);
|
||||
function cleanUp(){
|
||||
teardown(aMonitor).then(() => {
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
@ -794,7 +794,6 @@ InplaceEditor.prototype = {
|
||||
let direction = FOCUS_FORWARD;
|
||||
if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_TAB &&
|
||||
aEvent.shiftKey) {
|
||||
this.cancelled = true;
|
||||
direction = FOCUS_BACKWARD;
|
||||
}
|
||||
if (this.stopOnReturn && aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RETURN) {
|
||||
@ -858,7 +857,6 @@ InplaceEditor.prototype = {
|
||||
// Validate the entered value.
|
||||
this.warning.hidden = this.validate(this.input.value);
|
||||
this._applied = false;
|
||||
this._onBlur(null, true);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1449,8 +1449,9 @@ RuleEditor.prototype = {
|
||||
*/
|
||||
function TextPropertyEditor(aRuleEditor, aProperty)
|
||||
{
|
||||
this.doc = aRuleEditor.doc;
|
||||
this.popup = aRuleEditor.ruleView.popup;
|
||||
this.ruleEditor = aRuleEditor;
|
||||
this.doc = this.ruleEditor.doc;
|
||||
this.popup = this.ruleEditor.ruleView.popup;
|
||||
this.prop = aProperty;
|
||||
this.prop.editor = this;
|
||||
this.browserWindow = this.doc.defaultView.top;
|
||||
@ -1849,6 +1850,7 @@ TextPropertyEditor.prototype = {
|
||||
let name = this.prop.name;
|
||||
let value = typeof aValue == "undefined" ? this.prop.value : aValue;
|
||||
let val = this._parseValue(value);
|
||||
|
||||
let style = this.doc.createElementNS(HTML_NS, "div").style;
|
||||
let prefs = Services.prefs;
|
||||
|
||||
@ -1858,6 +1860,8 @@ TextPropertyEditor.prototype = {
|
||||
|
||||
try {
|
||||
style.setProperty(name, val.value, val.priority);
|
||||
// Live previewing the change without committing yet just yet, that'll be done in _onValueDone
|
||||
this.ruleEditor.rule.setPropertyValue(this.prop, val.value, val.priority);
|
||||
} finally {
|
||||
prefs.setBoolPref("layout.css.report_errors", prefVal);
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
browser_ruleview_override.js \
|
||||
browser_ruleview_ui.js \
|
||||
browser_ruleview_update.js \
|
||||
browser_ruleview_livepreview.js \
|
||||
browser_bug705707_is_content_stylesheet.js \
|
||||
browser_bug722196_property_view_media_queries.js \
|
||||
browser_bug722196_rule_view_media_queries.js \
|
||||
@ -41,6 +42,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
browser_bug893965_css_property_completion_existing_property.js \
|
||||
browser_bug894376_css_value_completion_new_property_value_pair.js \
|
||||
browser_bug894376_css_value_completion_existing_property_value_pair.js \
|
||||
browser_ruleview_bug_902966_revert_value_on_ESC.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
|
@ -29,7 +29,7 @@ let testData = [
|
||||
["VK_BACK_SPACE", {}, "", -1, 0],
|
||||
["c", {}, "caption-side", 0, 10],
|
||||
["o", {}, "color", 0, 6],
|
||||
["VK_TAB", {}, "n", -1, 0],
|
||||
["VK_TAB", {}, "none", -1, 0],
|
||||
["r", {}, "red", 0, 5],
|
||||
["VK_DOWN", {}, "rgb", 1, 5],
|
||||
["VK_DOWN", {}, "rgba", 2, 5],
|
||||
|
@ -0,0 +1,91 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test original value is correctly displayed when ESCaping out of the
|
||||
// inplace editor in the style inspector.
|
||||
|
||||
let doc;
|
||||
let ruleWindow;
|
||||
let ruleView;
|
||||
let inspector;
|
||||
let originalValue = "blue";
|
||||
|
||||
// Test data format
|
||||
// {
|
||||
// value: what char sequence to type,
|
||||
// commitKey: what key to type to "commit" the change,
|
||||
// modifiers: commitKey modifiers,
|
||||
// expected: what value is expected as a result
|
||||
// }
|
||||
let testData = [
|
||||
{value: "red", commitKey: "VK_ESCAPE", modifiers: {}, expected: originalValue},
|
||||
{value: "red", commitKey: "VK_RETURN", modifiers: {}, expected: "red"},
|
||||
{value: "blue", commitKey: "VK_TAB", modifiers: {shiftKey: true}, expected: "blue"}
|
||||
];
|
||||
|
||||
function startTests()
|
||||
{
|
||||
let style = '' +
|
||||
'#testid {' +
|
||||
' color: ' + originalValue + ';' +
|
||||
'}';
|
||||
|
||||
let styleNode = addStyle(doc, style);
|
||||
doc.body.innerHTML = '<div id="testid">Styled Node</div>';
|
||||
let testElement = doc.getElementById("testid");
|
||||
|
||||
openRuleView((aInspector, aRuleView) => {
|
||||
inspector = aInspector;
|
||||
ruleView = aRuleView;
|
||||
ruleWindow = aRuleView.doc.defaultView;
|
||||
inspector.selection.setNode(testElement);
|
||||
inspector.once("inspector-updated", () => runTestData(0));
|
||||
});
|
||||
}
|
||||
|
||||
function runTestData(index)
|
||||
{
|
||||
if (index === testData.length) {
|
||||
finishTest();
|
||||
return;
|
||||
}
|
||||
|
||||
let idRuleEditor = ruleView.element.children[1]._ruleEditor;
|
||||
let propEditor = idRuleEditor.rule.textProps[0].editor;
|
||||
waitForEditorFocus(propEditor.element, function(aEditor) {
|
||||
is(inplaceEditor(propEditor.valueSpan), aEditor, "Focused editor should be the value.");
|
||||
|
||||
for (let ch of testData[index].value) {
|
||||
EventUtils.sendChar(ch, ruleWindow);
|
||||
}
|
||||
EventUtils.synthesizeKey(testData[index].commitKey, testData[index].modifiers);
|
||||
|
||||
is(propEditor.valueSpan.innerHTML, testData[index].expected);
|
||||
|
||||
runTestData(index + 1);
|
||||
});
|
||||
|
||||
EventUtils.synthesizeMouse(propEditor.valueSpan, 1, 1, {}, ruleWindow);
|
||||
}
|
||||
|
||||
function finishTest()
|
||||
{
|
||||
inspector = ruleWindow = ruleView = null;
|
||||
doc = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function escapePropertyChange_load(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, escapePropertyChange_load, true);
|
||||
doc = content.document;
|
||||
waitForFocus(startTests, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,test escaping property change reverts back to original value";
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that changes are previewed when editing a property value
|
||||
|
||||
let doc;
|
||||
let testElement;
|
||||
let ruleWindow;
|
||||
let ruleView;
|
||||
let inspector;
|
||||
|
||||
// Format
|
||||
// {
|
||||
// value : what to type in the field
|
||||
// expected : expected computed style on the targeted element
|
||||
// }
|
||||
let testData = [
|
||||
{value: "inline", expected: "inline"},
|
||||
{value: "something", expected: "inline"}
|
||||
];
|
||||
|
||||
function startTest()
|
||||
{
|
||||
let style = '#testid {display:block;}';
|
||||
|
||||
let styleNode = addStyle(doc, style);
|
||||
doc.body.innerHTML = '<div id="testid">Styled Node</div><span>inline element</span>';
|
||||
testElement = doc.getElementById("testid");
|
||||
|
||||
openRuleView((aInspector, aRuleView) => {
|
||||
inspector = aInspector;
|
||||
ruleView = aRuleView;
|
||||
ruleWindow = aRuleView.doc.defaultView;
|
||||
inspector.selection.setNode(testElement);
|
||||
inspector.once("inspector-updated", () => loopTestData(0));
|
||||
});
|
||||
}
|
||||
|
||||
function loopTestData(index)
|
||||
{
|
||||
if(index === testData.length) {
|
||||
finishTest();
|
||||
return;
|
||||
}
|
||||
|
||||
let idRuleEditor = ruleView.element.children[1]._ruleEditor;
|
||||
let propEditor = idRuleEditor.rule.textProps[0].editor;
|
||||
waitForEditorFocus(propEditor.element, function(aEditor) {
|
||||
is(inplaceEditor(propEditor.valueSpan), aEditor, "Focused editor should be the value.");
|
||||
|
||||
// Entering a correct value for the property
|
||||
for (let ch of testData[index].value) {
|
||||
EventUtils.sendChar(ch, ruleWindow);
|
||||
}
|
||||
|
||||
// While the editor is still focused in, the display should have changed already
|
||||
executeSoon(() => {
|
||||
is(content.getComputedStyle(testElement).display,
|
||||
testData[index].expected,
|
||||
"Element should be previewed as " + testData[index].expected);
|
||||
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
loopTestData(index + 1);
|
||||
});
|
||||
});
|
||||
|
||||
EventUtils.synthesizeMouse(propEditor.valueSpan, 1, 1, {}, ruleWindow);
|
||||
}
|
||||
|
||||
function finishTest()
|
||||
{
|
||||
inspector = ruleWindow = ruleView = null;
|
||||
doc = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function changedValues_load(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, changedValues_load, true);
|
||||
doc = content.document;
|
||||
waitForFocus(startTest, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,test rule view live preview on user changes";
|
||||
}
|
@ -758,7 +758,6 @@ bin/libfreebl_32int64_3.so
|
||||
@BINPATH@/webapprt/chrome/@AB_CD@.manifest
|
||||
@BINPATH@/webapprt/components/CommandLineHandler.js
|
||||
@BINPATH@/webapprt/components/ContentPermission.js
|
||||
@BINPATH@/webapprt/components/ContentPolicy.js
|
||||
@BINPATH@/webapprt/components/DirectoryProvider.js
|
||||
@BINPATH@/webapprt/components/components.manifest
|
||||
@BINPATH@/webapprt/defaults/preferences/prefs.js
|
||||
|
@ -186,6 +186,16 @@
|
||||
- for the "Edit and Resend" menu item displayed in the context menu for a request -->
|
||||
<!ENTITY netmonitorUI.summary.editAndResend.accesskey "R">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.newTab): This is the label
|
||||
- for the Open in New Tab menu item displayed in the context menu of the
|
||||
- network container -->
|
||||
<!ENTITY netmonitorUI.context.newTab "Open in New Tab">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.newTab.accesskey): This is the access key
|
||||
- for the Open in New Tab menu item displayed in the context menu of the
|
||||
- network container -->
|
||||
<!ENTITY netmonitorUI.context.newTab.accesskey "O">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.custom.newRequest): This is the label displayed
|
||||
- as the title of the new custom request form -->
|
||||
<!ENTITY netmonitorUI.custom.newRequest "New Request">
|
||||
|
@ -19,19 +19,19 @@ var ContextUI = {
|
||||
*/
|
||||
|
||||
init: function init() {
|
||||
Elements.browsers.addEventListener("mousedown", this, true);
|
||||
Elements.browsers.addEventListener("touchstart", this, true);
|
||||
|
||||
Elements.browsers.addEventListener('URLChanged', this, true);
|
||||
Elements.tabList.addEventListener('TabSelect', this, true);
|
||||
Elements.panelUI.addEventListener('ToolPanelShown', this, false);
|
||||
Elements.panelUI.addEventListener('ToolPanelHidden', this, false);
|
||||
|
||||
window.addEventListener("touchstart", this, true);
|
||||
window.addEventListener("mousedown", this, true);
|
||||
window.addEventListener("MozEdgeUIStarted", this, true);
|
||||
window.addEventListener("MozEdgeUICanceled", this, true);
|
||||
window.addEventListener("MozEdgeUICompleted", this, true);
|
||||
window.addEventListener("keypress", this, true);
|
||||
window.addEventListener("KeyboardChanged", this, false);
|
||||
window.addEventListener("MozFlyoutPanelShowing", this, false);
|
||||
|
||||
Elements.tray.addEventListener("transitionend", this, true);
|
||||
|
||||
@ -248,8 +248,48 @@ var ContextUI = {
|
||||
this.toggleNavUI();
|
||||
},
|
||||
|
||||
onDownInput: function onDownInput(aEvent) {
|
||||
if (!this.isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Various ui element containers we do not update context ui for.
|
||||
let whitelist = [
|
||||
// Clicks on tab bar elements should not close the tab bar. the tabbar
|
||||
// handles this.
|
||||
Elements.tabs,
|
||||
// Don't let a click on an infobar button dismiss the appbar or navbar.
|
||||
// Note the notification box should always hover above these other two
|
||||
// bars.
|
||||
Browser.getNotificationBox()
|
||||
];
|
||||
|
||||
if (whitelist.some(elem => elem.contains(aEvent.target))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If a start tab is visible only dismiss the tab bar.
|
||||
if (BrowserUI.isStartTabVisible) {
|
||||
ContextUI.dismissTabs();
|
||||
return;
|
||||
}
|
||||
|
||||
// content, dismiss anything visible
|
||||
if (aEvent.target.ownerDocument.defaultView.top == getBrowser().contentWindow) {
|
||||
this.dismiss();
|
||||
return;
|
||||
}
|
||||
|
||||
// dismiss tabs and context app bar if visible
|
||||
this.dismissTabs();
|
||||
this.dismissContextAppbar();
|
||||
},
|
||||
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "URLChanged":
|
||||
this.displayNavbar();
|
||||
break;
|
||||
case "MozEdgeUIStarted":
|
||||
this._onEdgeUIStarted(aEvent);
|
||||
break;
|
||||
@ -273,13 +313,13 @@ var ContextUI = {
|
||||
this.dismissTabs();
|
||||
break;
|
||||
case "mousedown":
|
||||
if (BrowserUI.isStartTabVisible)
|
||||
if (aEvent.button != 0) {
|
||||
break;
|
||||
let box = Browser.getNotificationBox();
|
||||
if (!box.contains(aEvent.target) &&
|
||||
aEvent.button == 0 && this.isVisible) {
|
||||
this.dismiss();
|
||||
}
|
||||
this.onDownInput(aEvent);
|
||||
break;
|
||||
case "touchstart":
|
||||
this.onDownInput(aEvent);
|
||||
break;
|
||||
case "ToolPanelShown":
|
||||
case "ToolPanelHidden":
|
||||
@ -290,6 +330,14 @@ var ContextUI = {
|
||||
this.dismiss();
|
||||
}
|
||||
break;
|
||||
case "MozFlyoutPanelShowing":
|
||||
if (BrowserUI.isStartTabVisible) {
|
||||
this.dismissTabs();
|
||||
this.dismissContextAppbar();
|
||||
} else {
|
||||
this.dismiss();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -18,6 +18,14 @@ var NavButtonSlider = {
|
||||
_mouseDown: false,
|
||||
_yPos: -1,
|
||||
|
||||
get back() {
|
||||
return this._back;
|
||||
},
|
||||
|
||||
get plus() {
|
||||
return this._plus;
|
||||
},
|
||||
|
||||
/*
|
||||
* custom dragger, see input.js
|
||||
*/
|
||||
@ -72,7 +80,7 @@ var NavButtonSlider = {
|
||||
Services.prefs.addObserver(kNavButtonPref, this, false);
|
||||
},
|
||||
|
||||
observe: function BrowserUI_observe(aSubject, aTopic, aData) {
|
||||
observe: function (aSubject, aTopic, aData) {
|
||||
if (aTopic == "nsPref:changed" && aData == kNavButtonPref) {
|
||||
this._updateVisibility();
|
||||
}
|
||||
|
@ -554,7 +554,9 @@ let ContentScroll = {
|
||||
addMessageListener("Content:SetCacheViewport", this);
|
||||
addMessageListener("Content:SetWindowSize", this);
|
||||
|
||||
addEventListener("scroll", this, false);
|
||||
if (Services.prefs.getBoolPref("layers.async-pan-zoom.enabled")) {
|
||||
addEventListener("scroll", this, false);
|
||||
}
|
||||
addEventListener("pagehide", this, false);
|
||||
addEventListener("MozScrolledAreaChanged", this, false);
|
||||
},
|
||||
|
@ -1080,6 +1080,8 @@ var BrowserUI = {
|
||||
break;
|
||||
case "cmd_newTab":
|
||||
this.newTab(null, null, true);
|
||||
// Make sure navbar is displayed before setting focus on url bar. Bug 907244
|
||||
ContextUI.displayNavbar();
|
||||
this._edit.beginEditing(false);
|
||||
break;
|
||||
case "cmd_closeTab":
|
||||
|
@ -1558,12 +1558,18 @@ Tab.prototype = {
|
||||
},
|
||||
|
||||
startLoading: function startLoading() {
|
||||
if (this._loading) throw "Already Loading!";
|
||||
if (this._loading) {
|
||||
let stack = new Error().stack;
|
||||
throw "Already Loading!\n" + stack;
|
||||
}
|
||||
this._loading = true;
|
||||
},
|
||||
|
||||
endLoading: function endLoading() {
|
||||
if (!this._loading) throw "Not Loading!";
|
||||
if (!this._loading) {
|
||||
let stack = new Error().stack;
|
||||
throw "Not Loading!\n" + stack;
|
||||
}
|
||||
this._loading = false;
|
||||
this.updateFavicon();
|
||||
},
|
||||
|
@ -12,6 +12,8 @@ include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MOCHITEST_METRO_FILES = \
|
||||
head.js \
|
||||
helpers/BookmarksHelper.js \
|
||||
helpers/HistoryHelper.js \
|
||||
helpers/ViewStateHelper.js \
|
||||
browser_bookmarks.js \
|
||||
browser_canonizeURL.js \
|
||||
|
@ -31,99 +31,6 @@ function tearDown() {
|
||||
BookmarksTestHelper.restore();
|
||||
}
|
||||
|
||||
var BookmarksTestHelper = {
|
||||
_originalNavHistoryService: null,
|
||||
MockNavHistoryService: {
|
||||
getNewQueryOptions: function () {
|
||||
return {};
|
||||
},
|
||||
getNewQuery: function () {
|
||||
return {
|
||||
setFolders: function(){}
|
||||
};
|
||||
},
|
||||
executeQuery: function () {
|
||||
return {
|
||||
root: {
|
||||
get childCount() {
|
||||
return Object.keys(BookmarksTestHelper._nodes).length;
|
||||
},
|
||||
|
||||
getChild: function (aIndex) BookmarksTestHelper._nodes[Object.keys(BookmarksTestHelper._nodes)[aIndex]]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_originalBookmarkService: null,
|
||||
MockBookmarkService: {
|
||||
getItemIndex: function (aIndex) aIndex,
|
||||
getBookmarkURI: function (aId) BookmarksTestHelper._nodes[aId].uri,
|
||||
getItemTitle: function (aId) BookmarksTestHelper._nodes[aId].title,
|
||||
removeItem: function (aId) {
|
||||
delete BookmarksTestHelper._nodes[aId];
|
||||
|
||||
// Simulate observer notification
|
||||
gStartView._changes.onItemRemoved(aId, gStartView._root);
|
||||
},
|
||||
},
|
||||
|
||||
Node: function (aTitle, aId) {
|
||||
this.type = this.RESULT_TYPE_URI = 0;
|
||||
this.title = aTitle;
|
||||
this.itemId = aId;
|
||||
this.uri = "http://" + aTitle + ".com.br";
|
||||
this.pinned = true
|
||||
},
|
||||
|
||||
_nodes: null,
|
||||
createNodes: function (aMany) {
|
||||
this._nodes = {};
|
||||
for (let i=0; i<aMany; i++) {
|
||||
this._nodes[i] = new this.Node("Mock-Bookmark" + i, i);
|
||||
}
|
||||
},
|
||||
|
||||
_originalPinHelper: null,
|
||||
MockPinHelper: {
|
||||
isPinned: function (aItem) BookmarksTestHelper._nodes[aItem].pinned,
|
||||
setUnpinned: function (aItem) BookmarksTestHelper._nodes[aItem].pinned = false,
|
||||
setPinned: function (aItem) BookmarksTestHelper._nodes[aItem].pinned = true,
|
||||
},
|
||||
|
||||
_originalUpdateFavicon: null,
|
||||
setup: function setup() {
|
||||
// Just enough items so that there will be one less then the limit
|
||||
// after removing 4 items.
|
||||
this.createNodes(gStartView._limit + 3);
|
||||
|
||||
this._originalNavHistoryService = gStartView._navHistoryService;
|
||||
gStartView._navHistoryService = this.MockNavHistoryService;
|
||||
|
||||
this._originalBookmarkService = gStartView._bookmarkService;
|
||||
gStartView._bookmarkService= this.MockBookmarkService;
|
||||
|
||||
this._originalPinHelper = gStartView._pinHelper;
|
||||
gStartView._pinHelper = this.MockPinHelper;
|
||||
|
||||
this._originalUpdateFavicon = gStartView._updateFavicon;
|
||||
gStartView._updateFavicon = function () {};
|
||||
|
||||
gStartView.clearBookmarks();
|
||||
gStartView.getBookmarks();
|
||||
},
|
||||
|
||||
restore: function () {
|
||||
gStartView._navHistoryService = this._originalNavHistoryService;
|
||||
gStartView._bookmarkService= this._originalBookmarkService;
|
||||
gStartView._pinHelper = this._originalPinHelper;
|
||||
gStartView._updateFavicon = this._originalUpdateFavicon;
|
||||
|
||||
gStartView.clearBookmarks();
|
||||
gStartView.getBookmarks();
|
||||
}
|
||||
};
|
||||
|
||||
gTests.push({
|
||||
desc: "Test bookmarks StartUI unpin",
|
||||
setUp: setup,
|
||||
|
@ -9,10 +9,20 @@ function test() {
|
||||
runTests();
|
||||
}
|
||||
|
||||
function doEdgeUIGesture() {
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("MozEdgeUICompleted", true, false);
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
|
||||
function getpage(idx) {
|
||||
return "http://mochi.test:8888/metro/browser/metro/base/tests/mochitest/" + "res/blankpage" + idx + ".html";
|
||||
}
|
||||
|
||||
gTests.push({
|
||||
desc: "Context UI on about:start",
|
||||
run: function testAboutStart() {
|
||||
yield addTab("about:start");
|
||||
let tab = yield addTab("about:start");
|
||||
|
||||
yield waitForCondition(function () {
|
||||
return BrowserUI.isStartTabVisible;
|
||||
@ -42,13 +52,15 @@ gTests.push({
|
||||
is(ContextUI.contextAppbarVisible, false, "Appbar is hidden after third swipe");
|
||||
|
||||
is(BrowserUI.isStartTabVisible, true, "Start UI is still visible");
|
||||
|
||||
Browser.closeTab(tab, { forceClose: true });
|
||||
}
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "Context UI on a web page (about:)",
|
||||
run: function testAbout() {
|
||||
yield addTab("about:");
|
||||
let tab = yield addTab("about:");
|
||||
ContextUI.dismiss();
|
||||
is(BrowserUI.isStartTabVisible, false, "Start UI is not visible on about:");
|
||||
is(ContextUI.navbarVisible, false, "Navbar is not initially visible on about:");
|
||||
@ -63,6 +75,8 @@ gTests.push({
|
||||
is(ContextUI.tabbarVisible, false, "Tabbar is not visible after second swipe");
|
||||
|
||||
is(BrowserUI.isStartTabVisible, false, "Start UI is still not visible");
|
||||
|
||||
Browser.closeTab(tab, { forceClose: true });
|
||||
}
|
||||
});
|
||||
|
||||
@ -96,8 +110,125 @@ gTests.push({
|
||||
}
|
||||
});
|
||||
|
||||
function doEdgeUIGesture() {
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("MozEdgeUICompleted", true, false);
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
gTests.push({
|
||||
desc: "taps vs context ui dismissal",
|
||||
run: function () {
|
||||
// off by default
|
||||
InputSourceHelper.isPrecise = false;
|
||||
InputSourceHelper.fireUpdate();
|
||||
|
||||
let tab = yield addTab("about:mozilla");
|
||||
|
||||
ok(ContextUI.navbarVisible, "navbar visible after open");
|
||||
|
||||
let navButtonDisplayPromise = waitForEvent(NavButtonSlider.back, "transitionend");
|
||||
|
||||
yield loadUriInActiveTab(getpage(1));
|
||||
|
||||
is(tab.browser.currentURI.spec, getpage(1), getpage(1));
|
||||
ok(ContextUI.navbarVisible, "navbar visible after navigate 1");
|
||||
|
||||
yield loadUriInActiveTab(getpage(2));
|
||||
|
||||
is(tab.browser.currentURI.spec, getpage(2), getpage(2));
|
||||
ok(ContextUI.navbarVisible, "navbar visible after navigate 2");
|
||||
|
||||
yield loadUriInActiveTab(getpage(3));
|
||||
|
||||
is(tab.browser.currentURI.spec, getpage(3), getpage(3));
|
||||
ok(ContextUI.navbarVisible, "navbar visible after navigate 3");
|
||||
|
||||
// These transition in after we navigate. If we click on one of
|
||||
// them before they are visible they don't work, so wait for
|
||||
// display to occur.
|
||||
yield navButtonDisplayPromise;
|
||||
|
||||
yield navBackViaNavButton();
|
||||
yield waitForCondition2(function () { return tab.browser.currentURI.spec == getpage(2); }, "getpage(2)");
|
||||
yield waitForCondition2(function () { return ContextUI.navbarVisible; }, "ContextUI.navbarVisible");
|
||||
|
||||
is(tab.browser.currentURI.spec, getpage(2), getpage(2));
|
||||
|
||||
yield navForward();
|
||||
yield waitForCondition2(function () { return tab.browser.currentURI.spec == getpage(3); }, "getpage(3)");
|
||||
|
||||
is(tab.browser.currentURI.spec, getpage(3), getpage(3));
|
||||
ok(ContextUI.navbarVisible, "navbar visible after navigate");
|
||||
|
||||
doEdgeUIGesture();
|
||||
|
||||
is(ContextUI.navbarVisible, true, "Navbar is visible after swipe");
|
||||
is(ContextUI.tabbarVisible, true, "Tabbar is visible after swipe");
|
||||
|
||||
yield navBackViaNavButton();
|
||||
yield waitForCondition2(function () { return tab.browser.currentURI.spec == getpage(2); }, "getpage(2)");
|
||||
|
||||
is(tab.browser.currentURI.spec, getpage(2), getpage(2));
|
||||
is(ContextUI.navbarVisible, true, "Navbar is visible after navigating back (overlay)");
|
||||
yield waitForCondition2(function () { return !ContextUI.tabbarVisible; }, "!ContextUI.tabbarVisible");
|
||||
|
||||
sendElementTap(window, window.document.documentElement);
|
||||
yield waitForCondition2(function () { return !BrowserUI.navbarVisible; }, "!BrowserUI.navbarVisible");
|
||||
|
||||
is(ContextUI.tabbarVisible, false, "Tabbar is hidden after content tap");
|
||||
|
||||
yield navForward();
|
||||
yield waitForCondition2(function () {
|
||||
return tab.browser.currentURI.spec == getpage(3) && ContextUI.navbarVisible;
|
||||
}, "getpage(3)");
|
||||
|
||||
is(tab.browser.currentURI.spec, getpage(3), getpage(3));
|
||||
ok(ContextUI.navbarVisible, "navbar visible after navigate");
|
||||
|
||||
yield navBackViaNavButton();
|
||||
yield waitForCondition2(function () { return tab.browser.currentURI.spec == getpage(2); }, "getpage(2)");
|
||||
yield waitForCondition2(function () { return !ContextUI.tabbarVisible; }, "!ContextUI.tabbarVisible");
|
||||
|
||||
is(tab.browser.currentURI.spec, getpage(2), getpage(2));
|
||||
is(ContextUI.navbarVisible, true, "Navbar is visible after navigating back (overlay)");
|
||||
|
||||
ContextUI.dismiss();
|
||||
|
||||
let note = yield showNotification();
|
||||
doEdgeUIGesture();
|
||||
sendElementTap(window, note);
|
||||
|
||||
is(ContextUI.navbarVisible, true, "Navbar is visible after clicking notification close button");
|
||||
|
||||
removeNotifications();
|
||||
|
||||
Browser.closeTab(tab, { forceClose: true });
|
||||
}
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "Bug 907244 - Opening a new tab when the page has focus doesn't correctly focus the location bar",
|
||||
run: function () {
|
||||
let mozTab = yield addTab("about:mozilla");
|
||||
|
||||
// addTab will dismiss navbar, but lets check anyway.
|
||||
ok(!ContextUI.navbarVisible, "navbar dismissed");
|
||||
|
||||
BrowserUI.doCommand("cmd_newTab");
|
||||
let newTab = Browser.selectedTab;
|
||||
yield newTab.pageShowPromise;
|
||||
|
||||
yield waitForCondition(() => ContextUI.navbarVisible);
|
||||
ok(ContextUI.navbarVisible, "navbar visible");
|
||||
|
||||
let edit = document.getElementById("urlbar-edit");
|
||||
|
||||
ok(edit.focused, "Edit has focus");
|
||||
|
||||
// Lets traverse since node.contains() doesn't work for anonymous elements.
|
||||
let parent = document.activeElement;
|
||||
while(parent && parent != edit) {
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
|
||||
ok(parent === edit, 'Active element is a descendant of urlbar');
|
||||
|
||||
Browser.closeTab(newTab, { forceClose: true });
|
||||
Browser.closeTab(mozTab, { forceClose: true });
|
||||
}
|
||||
});
|
||||
|
@ -38,92 +38,6 @@ function tearDown() {
|
||||
HistoryTestHelper.restore();
|
||||
}
|
||||
|
||||
var HistoryTestHelper = {
|
||||
_originalNavHistoryService: null,
|
||||
MockNavHistoryService: {
|
||||
getNewQueryOptions: function () {
|
||||
return {};
|
||||
},
|
||||
getNewQuery: function () {
|
||||
return {
|
||||
setFolders: function(){}
|
||||
};
|
||||
},
|
||||
executeQuery: function () {
|
||||
return {
|
||||
root: {
|
||||
get childCount() {
|
||||
return Object.keys(HistoryTestHelper._nodes).length;
|
||||
},
|
||||
|
||||
getChild: function (aIndex) HistoryTestHelper._nodes[Object.keys(HistoryTestHelper._nodes)[aIndex]]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_originalHistoryService: null,
|
||||
MockHistoryService: {
|
||||
removePage: function (aURI) {
|
||||
delete HistoryTestHelper._nodes[aURI.spec];
|
||||
|
||||
// Simulate observer notification
|
||||
gStartView.onDeleteURI(aURI);
|
||||
},
|
||||
},
|
||||
|
||||
Node: function (aTitle, aURISpec) {
|
||||
this.title = aTitle;
|
||||
this.uri = aURISpec;
|
||||
this.pinned = true
|
||||
},
|
||||
|
||||
_nodes: null,
|
||||
createNodes: function (aMany) {
|
||||
this._nodes = {};
|
||||
for (let i=0; i<aMany; i++) {
|
||||
let title = "mock-history-" + i;
|
||||
let uri = "http://" + title + ".com.br/";
|
||||
|
||||
this._nodes[uri] = new this.Node(title, uri);
|
||||
}
|
||||
},
|
||||
|
||||
_originalPinHelper: null,
|
||||
MockPinHelper: {
|
||||
isPinned: function (aItem) HistoryTestHelper._nodes[aItem].pinned,
|
||||
setUnpinned: function (aItem) HistoryTestHelper._nodes[aItem].pinned = false,
|
||||
setPinned: function (aItem) HistoryTestHelper._nodes[aItem].pinned = true,
|
||||
},
|
||||
|
||||
setup: function setup() {
|
||||
// Just enough items so that there will be one less then the limit
|
||||
// after removing 4 items.
|
||||
this.createNodes(gStartView._limit + 3);
|
||||
|
||||
this._originalNavHistoryService = gStartView._navHistoryService;
|
||||
gStartView._navHistoryService = this.MockNavHistoryService;
|
||||
|
||||
this._originalHistoryService = gStartView._historyService;
|
||||
gStartView._historyService= this.MockHistoryService;
|
||||
|
||||
this._originalPinHelper = gStartView._pinHelper;
|
||||
gStartView._pinHelper = this.MockPinHelper;
|
||||
|
||||
gStartView._set.clearAll();
|
||||
gStartView.populateGrid();
|
||||
},
|
||||
|
||||
restore: function () {
|
||||
gStartView._navHistoryService = this._originalNavHistoryService;
|
||||
gStartView._historyService= this._originalHistoryService;
|
||||
gStartView._pinHelper = this._originalPinHelper;
|
||||
|
||||
gStartView._set.clearAll();
|
||||
gStartView.populateGrid();
|
||||
}
|
||||
};
|
||||
|
||||
function uriFromIndex(aIndex) {
|
||||
return "http://mock-history-" + aIndex + ".com.br/"
|
||||
}
|
||||
|
@ -95,9 +95,35 @@ gTests.push({
|
||||
|
||||
gTests.push({
|
||||
desc: "Test Snapped scrolls vertically",
|
||||
setUp: function() {
|
||||
|
||||
// Populate with mock data and expand bookmarks
|
||||
BookmarksTestHelper.setup();
|
||||
sendElementTap(Browser.selectedBrowser.contentWindow, getNarrowTitle("start-bookmarks"));
|
||||
|
||||
yield waitForCondition(() => gStartDoc.getElementById("start-bookmarks").hasAttribute("expanded"));
|
||||
|
||||
yield setUpSnapped();
|
||||
},
|
||||
run: function() {
|
||||
ok(Browser.selectedBrowser.contentWindow.scrollMaxY !== 0, "Snapped scrolls vertically");
|
||||
ok(Browser.selectedBrowser.contentWindow.scrollMaxX === 0, "Snapped does not scroll horizontally");
|
||||
},
|
||||
tearDown: function() {
|
||||
BookmarksTestHelper.restore();
|
||||
restoreViewstate();
|
||||
}
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "Navbar contextual buttons are not shown in snapped",
|
||||
setUp: setUpSnapped,
|
||||
run: function() {
|
||||
todo(false, "We need to populate startUI to verify that it's scrollable");
|
||||
let toolbarContextual = document.getElementById("toolbar-contextual");
|
||||
|
||||
let visibility = getComputedStyle(toolbarContextual).getPropertyValue("visibility");
|
||||
|
||||
ok(visibility === "collapse" || visibility === "hidden", "Contextual buttons not shown in navbar");
|
||||
},
|
||||
tearDown: restoreViewstate
|
||||
});
|
||||
@ -117,3 +143,23 @@ gTests.push({
|
||||
},
|
||||
tearDown: restoreViewstate
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "Test portrait scrolls vertically",
|
||||
setUp: function() {
|
||||
// Populate with mock data
|
||||
BookmarksTestHelper.setup();
|
||||
HistoryTestHelper.setup();
|
||||
|
||||
yield setUpPortrait();
|
||||
},
|
||||
run: function() {
|
||||
ok(Browser.selectedBrowser.contentWindow.scrollMaxY !== 0, "Portrait scrolls vertically");
|
||||
ok(Browser.selectedBrowser.contentWindow.scrollMaxX === 0, "Portrait does not scroll horizontally");
|
||||
},
|
||||
tearDown: function() {
|
||||
BookmarksTestHelper.restore();
|
||||
HistoryTestHelper.restore();
|
||||
restoreViewstate();
|
||||
}
|
||||
});
|
||||
|
@ -34,6 +34,8 @@ splitPath.push('mochitest');
|
||||
const mochitestPath = splitPath.join('/') + '/';
|
||||
|
||||
[
|
||||
"BookmarksHelper.js",
|
||||
"HistoryHelper.js",
|
||||
"ViewStateHelper.js"
|
||||
].forEach(function(lib) {
|
||||
Services.scriptloader.loadSubScript(mochitestPath + lib, this);
|
||||
@ -112,39 +114,39 @@ function checkMonoclePositionRange(aMonocle, aMinX, aMaxX, aMinY, aMaxY)
|
||||
function showNotification()
|
||||
{
|
||||
return Task.spawn(function() {
|
||||
try {
|
||||
let strings = Strings.browser;
|
||||
var buttons = [
|
||||
{
|
||||
isDefault: false,
|
||||
label: strings.GetStringFromName("popupButtonAllowOnce2"),
|
||||
accessKey: "",
|
||||
callback: function() { }
|
||||
},
|
||||
{
|
||||
label: strings.GetStringFromName("popupButtonAlwaysAllow3"),
|
||||
accessKey: "",
|
||||
callback: function() { }
|
||||
},
|
||||
{
|
||||
label: strings.GetStringFromName("popupButtonNeverWarn3"),
|
||||
accessKey: "",
|
||||
callback: function() { }
|
||||
}
|
||||
];
|
||||
let notificationBox = Browser.getNotificationBox();
|
||||
const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
|
||||
notificationBox.appendNotification("test notification", "popup-blocked",
|
||||
"chrome://browser/skin/images/infobar-popup.png",
|
||||
priority, buttons);
|
||||
yield waitForEvent(notificationBox, "transitionend");
|
||||
return;
|
||||
} catch (ex) {
|
||||
throw new Task.Result(ex);
|
||||
}
|
||||
let strings = Strings.browser;
|
||||
var buttons = [
|
||||
{
|
||||
isDefault: false,
|
||||
label: strings.GetStringFromName("popupButtonAllowOnce2"),
|
||||
accessKey: "",
|
||||
callback: function() { }
|
||||
},
|
||||
{
|
||||
label: strings.GetStringFromName("popupButtonAlwaysAllow3"),
|
||||
accessKey: "",
|
||||
callback: function() { }
|
||||
},
|
||||
{
|
||||
label: strings.GetStringFromName("popupButtonNeverWarn3"),
|
||||
accessKey: "",
|
||||
callback: function() { }
|
||||
}
|
||||
];
|
||||
let notificationBox = Browser.getNotificationBox();
|
||||
const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
|
||||
let note = notificationBox.appendNotification("test notification", "popup-blocked",
|
||||
"chrome://browser/skin/images/infobar-popup.png",
|
||||
priority, buttons);
|
||||
yield waitForEvent(notificationBox, "transitionend");
|
||||
throw new Task.Result(note);
|
||||
});
|
||||
}
|
||||
|
||||
function removeNotifications() {
|
||||
Browser.getNotificationBox().removeAllNotifications(true);
|
||||
}
|
||||
|
||||
function getSelection(aElement) {
|
||||
if (!aElement)
|
||||
return null;
|
||||
@ -188,10 +190,6 @@ function clearSelection(aTarget) {
|
||||
purgeEventQueue();
|
||||
}
|
||||
|
||||
/*=============================================================================
|
||||
Asynchronous Metro ui helpers
|
||||
=============================================================================*/
|
||||
|
||||
// Hides the tab and context app bar if they are visible
|
||||
function hideContextUI()
|
||||
{
|
||||
@ -233,10 +231,39 @@ function fireAppBarDisplayEvent()
|
||||
}
|
||||
|
||||
/*=============================================================================
|
||||
Asynchronous test helpers
|
||||
General test helpers
|
||||
=============================================================================*/
|
||||
let gOpenedTabs = [];
|
||||
|
||||
function loadUriInActiveTab(aUri)
|
||||
{
|
||||
return Task.spawn(function() {
|
||||
let promise = waitForEvent(getBrowser(), "pageshow");
|
||||
BrowserUI.goToURI(aUri);
|
||||
yield waitForCondition(function () {
|
||||
return getBrowser().currentURI.spec == aUri
|
||||
}, "getBrowser().currentURI.spec == " + aUri);
|
||||
yield promise;
|
||||
});
|
||||
}
|
||||
|
||||
function navForward() {
|
||||
return Task.spawn(function() {
|
||||
let promise = waitForEvent(getBrowser(), "pageshow");
|
||||
EventUtils.synthesizeKey("VK_RIGHT", { altKey: true }, window);
|
||||
yield promise;
|
||||
});
|
||||
}
|
||||
|
||||
function navBackViaNavButton() {
|
||||
return Task.spawn(function() {
|
||||
let promise = waitForEvent(getBrowser(), "pageshow");
|
||||
let backButton = document.getElementById("overlay-back");
|
||||
sendElementTap(window, backButton);
|
||||
yield promise;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a URL in a new tab asynchronously.
|
||||
*
|
||||
@ -301,11 +328,11 @@ function waitForEvent(aSubject, aEventName, aTimeoutMs, aTarget) {
|
||||
let timeoutMs = aTimeoutMs || kDefaultWait;
|
||||
let stack = new Error().stack;
|
||||
let timerID = setTimeout(function wfe_canceller() {
|
||||
aSubject.removeEventListener(aEventName, onEvent);
|
||||
aSubject.removeEventListener(aEventName, listener);
|
||||
eventDeferred.reject( new Error(aEventName+" event timeout at " + stack) );
|
||||
}, timeoutMs);
|
||||
|
||||
function onEvent(aEvent) {
|
||||
var listener = function (aEvent) {
|
||||
if (aTarget && aTarget !== aEvent.target)
|
||||
return;
|
||||
|
||||
@ -316,11 +343,11 @@ function waitForEvent(aSubject, aEventName, aTimeoutMs, aTarget) {
|
||||
|
||||
function cleanup() {
|
||||
// unhook listener in case of success or failure
|
||||
aSubject.removeEventListener(aEventName, onEvent);
|
||||
aSubject.removeEventListener(aEventName, listener);
|
||||
}
|
||||
eventDeferred.promise.then(cleanup, cleanup);
|
||||
|
||||
aSubject.addEventListener(aEventName, onEvent, false);
|
||||
aSubject.addEventListener(aEventName, listener, false);
|
||||
return eventDeferred.promise;
|
||||
}
|
||||
|
||||
@ -397,6 +424,50 @@ function waitForCondition(aCondition, aTimeoutMs, aIntervalMs) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* same as waitForCondition but with better test output.
|
||||
*
|
||||
* @param aCondition the callback that must return a truthy value
|
||||
* @param aTestMsg test condition message printed when the test succeeds or
|
||||
* fails. Defaults to the stringified version of aCondition.
|
||||
* @param aTimeoutMs the number of miliseconds to wait before giving up
|
||||
* @param aIntervalMs the number of miliseconds between calls to aCondition
|
||||
* @returns a Promise that resolves to true, or to an Error
|
||||
*/
|
||||
function waitForCondition2(aCondition, aTestMsg, aTimeoutMs, aIntervalMs) {
|
||||
let deferred = Promise.defer();
|
||||
let msg = aTestMsg || aCondition;
|
||||
let timeoutMs = aTimeoutMs || kDefaultWait;
|
||||
let intervalMs = aIntervalMs || kDefaultInterval;
|
||||
let startTime = Date.now();
|
||||
|
||||
function testCondition() {
|
||||
let now = Date.now();
|
||||
if((now - startTime) > timeoutMs) {
|
||||
deferred.reject( new Error("Timed out waiting for " + msg) );
|
||||
return;
|
||||
}
|
||||
|
||||
let condition;
|
||||
try {
|
||||
condition = aCondition();
|
||||
} catch (e) {
|
||||
deferred.reject( new Error("Got exception while attempting to test '" + msg + "': " + e) );
|
||||
return;
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
ok(true, msg);
|
||||
deferred.resolve(true);
|
||||
} else {
|
||||
setTimeout(testCondition, intervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(testCondition, 0);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/*
|
||||
* Waits for an image in a page to load. Wrapper around waitForCondition.
|
||||
*
|
||||
|
101
browser/metro/base/tests/mochitest/helpers/BookmarksHelper.js
Normal file
101
browser/metro/base/tests/mochitest/helpers/BookmarksHelper.js
Normal file
@ -0,0 +1,101 @@
|
||||
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
var BookmarksTestHelper = {
|
||||
_originalNavHistoryService: null,
|
||||
_startView: null,
|
||||
MockNavHistoryService: {
|
||||
getNewQueryOptions: function () {
|
||||
return {};
|
||||
},
|
||||
getNewQuery: function () {
|
||||
return {
|
||||
setFolders: function(){}
|
||||
};
|
||||
},
|
||||
executeQuery: function () {
|
||||
return {
|
||||
root: {
|
||||
get childCount() {
|
||||
return Object.keys(BookmarksTestHelper._nodes).length;
|
||||
},
|
||||
|
||||
getChild: function (aIndex) BookmarksTestHelper._nodes[Object.keys(BookmarksTestHelper._nodes)[aIndex]]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_originalBookmarkService: null,
|
||||
MockBookmarkService: {
|
||||
getItemIndex: function (aIndex) aIndex,
|
||||
getBookmarkURI: function (aId) BookmarksTestHelper._nodes[aId].uri,
|
||||
getItemTitle: function (aId) BookmarksTestHelper._nodes[aId].title,
|
||||
removeItem: function (aId) {
|
||||
delete BookmarksTestHelper._nodes[aId];
|
||||
|
||||
// Simulate observer notification
|
||||
BookmarksTestHelper._startView._changes.onItemRemoved(aId, BookmarksTestHelper._startView._root);
|
||||
},
|
||||
},
|
||||
|
||||
Node: function (aTitle, aId) {
|
||||
this.type = this.RESULT_TYPE_URI = 0;
|
||||
this.title = aTitle;
|
||||
this.itemId = aId;
|
||||
this.uri = "http://" + aTitle + ".com.br";
|
||||
this.pinned = true
|
||||
},
|
||||
|
||||
_nodes: null,
|
||||
createNodes: function (aMany) {
|
||||
this._nodes = {};
|
||||
for (let i=0; i<aMany; i++) {
|
||||
this._nodes[i] = new this.Node("Mock-Bookmark" + i, i);
|
||||
}
|
||||
},
|
||||
|
||||
_originalPinHelper: null,
|
||||
MockPinHelper: {
|
||||
isPinned: function (aItem) BookmarksTestHelper._nodes[aItem].pinned,
|
||||
setUnpinned: function (aItem) BookmarksTestHelper._nodes[aItem].pinned = false,
|
||||
setPinned: function (aItem) BookmarksTestHelper._nodes[aItem].pinned = true,
|
||||
},
|
||||
|
||||
_originalUpdateFavicon: null,
|
||||
setup: function setup() {
|
||||
this._startView = Browser.selectedBrowser.contentWindow.BookmarksStartView._view;
|
||||
|
||||
// Just enough items so that there will be one less then the limit
|
||||
// after removing 4 items.
|
||||
this.createNodes(this._startView._limit + 3);
|
||||
|
||||
this._originalNavHistoryService = this._startView._navHistoryService;
|
||||
this._startView._navHistoryService = this.MockNavHistoryService;
|
||||
|
||||
this._originalBookmarkService = this._startView._bookmarkService;
|
||||
this._startView._bookmarkService= this.MockBookmarkService;
|
||||
|
||||
this._originalPinHelper = this._startView._pinHelper;
|
||||
this._startView._pinHelper = this.MockPinHelper;
|
||||
|
||||
this._originalUpdateFavicon = this._startView._updateFavicon;
|
||||
this._startView._updateFavicon = function () {};
|
||||
|
||||
this._startView.clearBookmarks();
|
||||
this._startView.getBookmarks();
|
||||
},
|
||||
|
||||
restore: function () {
|
||||
this._startView._navHistoryService = this._originalNavHistoryService;
|
||||
this._startView._bookmarkService= this._originalBookmarkService;
|
||||
this._startView._pinHelper = this._originalPinHelper;
|
||||
this._startView._updateFavicon = this._originalUpdateFavicon;
|
||||
|
||||
this._startView.clearBookmarks();
|
||||
this._startView.getBookmarks();
|
||||
}
|
||||
};
|
94
browser/metro/base/tests/mochitest/helpers/HistoryHelper.js
Normal file
94
browser/metro/base/tests/mochitest/helpers/HistoryHelper.js
Normal file
@ -0,0 +1,94 @@
|
||||
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
var HistoryTestHelper = {
|
||||
_originalNavHistoryService: null,
|
||||
_startView: null,
|
||||
MockNavHistoryService: {
|
||||
getNewQueryOptions: function () {
|
||||
return {};
|
||||
},
|
||||
getNewQuery: function () {
|
||||
return {
|
||||
setFolders: function(){}
|
||||
};
|
||||
},
|
||||
executeQuery: function () {
|
||||
return {
|
||||
root: {
|
||||
get childCount() {
|
||||
return Object.keys(HistoryTestHelper._nodes).length;
|
||||
},
|
||||
|
||||
getChild: function (aIndex) HistoryTestHelper._nodes[Object.keys(HistoryTestHelper._nodes)[aIndex]]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_originalHistoryService: null,
|
||||
MockHistoryService: {
|
||||
removePage: function (aURI) {
|
||||
delete HistoryTestHelper._nodes[aURI.spec];
|
||||
|
||||
// Simulate observer notification
|
||||
HistoryTestHelper._startView.onDeleteURI(aURI);
|
||||
},
|
||||
},
|
||||
|
||||
Node: function (aTitle, aURISpec) {
|
||||
this.title = aTitle;
|
||||
this.uri = aURISpec;
|
||||
this.pinned = true
|
||||
},
|
||||
|
||||
_nodes: null,
|
||||
createNodes: function (aMany) {
|
||||
this._nodes = {};
|
||||
for (let i=0; i<aMany; i++) {
|
||||
let title = "mock-history-" + i;
|
||||
let uri = "http://" + title + ".com.br/";
|
||||
|
||||
this._nodes[uri] = new this.Node(title, uri);
|
||||
}
|
||||
},
|
||||
|
||||
_originalPinHelper: null,
|
||||
MockPinHelper: {
|
||||
isPinned: function (aItem) HistoryTestHelper._nodes[aItem].pinned,
|
||||
setUnpinned: function (aItem) HistoryTestHelper._nodes[aItem].pinned = false,
|
||||
setPinned: function (aItem) HistoryTestHelper._nodes[aItem].pinned = true,
|
||||
},
|
||||
|
||||
setup: function setup() {
|
||||
this._startView = Browser.selectedBrowser.contentWindow.HistoryStartView._view;
|
||||
|
||||
// Just enough items so that there will be one less then the limit
|
||||
// after removing 4 items.
|
||||
this.createNodes(this._startView._limit + 3);
|
||||
|
||||
this._originalNavHistoryService = this._startView._navHistoryService;
|
||||
this._startView._navHistoryService = this.MockNavHistoryService;
|
||||
|
||||
this._originalHistoryService = this._startView._historyService;
|
||||
this._startView._historyService= this.MockHistoryService;
|
||||
|
||||
this._originalPinHelper = this._startView._pinHelper;
|
||||
this._startView._pinHelper = this.MockPinHelper;
|
||||
|
||||
this._startView._set.clearAll();
|
||||
this._startView.populateGrid();
|
||||
},
|
||||
|
||||
restore: function () {
|
||||
this._startView._navHistoryService = this._originalNavHistoryService;
|
||||
this._startView._historyService= this._originalHistoryService;
|
||||
this._startView._pinHelper = this._originalPinHelper;
|
||||
|
||||
this._startView._set.clearAll();
|
||||
this._startView.populateGrid();
|
||||
}
|
||||
};
|
@ -6,14 +6,11 @@
|
||||
"use strict";
|
||||
|
||||
const snappedSize = 330;
|
||||
const portraitSize = 660;
|
||||
const portraitSize = 900;
|
||||
|
||||
function setSnappedViewstate() {
|
||||
ok(isLandscapeMode(), "setSnappedViewstate expects landscape mode to work.");
|
||||
|
||||
// Communicate viewstate change
|
||||
Services.obs.notifyObservers(null, 'metro_viewstate_changed', 'snapped');
|
||||
|
||||
let browser = Browser.selectedBrowser;
|
||||
|
||||
// Reduce browser width to simulate small screen size.
|
||||
@ -22,6 +19,9 @@ function setSnappedViewstate() {
|
||||
|
||||
browser.style.borderRight = padding + "px solid gray";
|
||||
|
||||
// Communicate viewstate change
|
||||
Services.obs.notifyObservers(null, 'metro_viewstate_changed', 'snapped');
|
||||
|
||||
// Make sure it renders the new mode properly
|
||||
yield waitForMs(0);
|
||||
}
|
||||
@ -29,8 +29,6 @@ function setSnappedViewstate() {
|
||||
function setPortraitViewstate() {
|
||||
ok(isLandscapeMode(), "setPortraitViewstate expects landscape mode to work.");
|
||||
|
||||
Services.obs.notifyObservers(null, 'metro_viewstate_changed', 'portrait');
|
||||
|
||||
let browser = Browser.selectedBrowser;
|
||||
|
||||
let fullWidth = browser.clientWidth;
|
||||
@ -38,6 +36,8 @@ function setPortraitViewstate() {
|
||||
|
||||
browser.style.borderRight = padding + "px solid gray";
|
||||
|
||||
Services.obs.notifyObservers(null, 'metro_viewstate_changed', 'portrait');
|
||||
|
||||
// Make sure it renders the new mode properly
|
||||
yield waitForMs(0);
|
||||
}
|
||||
|
@ -16,6 +16,9 @@ MOCHITEST_METRO_FILES = \
|
||||
textinput01.html \
|
||||
textarea01.html \
|
||||
testEngine.xml \
|
||||
blankpage1.html \
|
||||
blankpage2.html \
|
||||
blankpage3.html \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
14
browser/metro/base/tests/mochitest/res/blankpage1.html
Normal file
14
browser/metro/base/tests/mochitest/res/blankpage1.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
page 1</body>
|
||||
</html>
|
14
browser/metro/base/tests/mochitest/res/blankpage2.html
Normal file
14
browser/metro/base/tests/mochitest/res/blankpage2.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
page 2</body>
|
||||
</html>
|
14
browser/metro/base/tests/mochitest/res/blankpage3.html
Normal file
14
browser/metro/base/tests/mochitest/res/blankpage3.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
page 3</body>
|
||||
</html>
|
@ -262,8 +262,8 @@ this.Social = {
|
||||
SocialService.installProvider(doc, data, installCallback);
|
||||
},
|
||||
|
||||
uninstallProvider: function(origin) {
|
||||
SocialService.uninstallProvider(origin);
|
||||
uninstallProvider: function(origin, aCallback) {
|
||||
SocialService.uninstallProvider(origin, aCallback);
|
||||
},
|
||||
|
||||
// Activation functionality
|
||||
|
@ -14,18 +14,36 @@ Cu.import("resource://gre/modules/Webapps.jsm");
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
Cu.import("resource://gre/modules/WebappsInstaller.jsm");
|
||||
Cu.import("resource://gre/modules/WebappOSUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
this.webappsUI = {
|
||||
downloads: {},
|
||||
|
||||
init: function webappsUI_init() {
|
||||
Services.obs.addObserver(this, "webapps-ask-install", false);
|
||||
Services.obs.addObserver(this, "webapps-launch", false);
|
||||
Services.obs.addObserver(this, "webapps-uninstall", false);
|
||||
cpmm.addMessageListener("Webapps:OfflineCache", this);
|
||||
},
|
||||
|
||||
uninit: function webappsUI_uninit() {
|
||||
Services.obs.removeObserver(this, "webapps-ask-install");
|
||||
Services.obs.removeObserver(this, "webapps-launch");
|
||||
Services.obs.removeObserver(this, "webapps-uninstall");
|
||||
cpmm.removeMessageListener("Webapps:OfflineCache", this);
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
let data = aMessage.data;
|
||||
|
||||
if (aMessage.name == "Webapps:OfflineCache" && data.installState == "installed") {
|
||||
this.downloads[data.manifest].resolve();
|
||||
}
|
||||
},
|
||||
|
||||
observe: function webappsUI_observe(aSubject, aTopic, aData) {
|
||||
@ -103,7 +121,12 @@ this.webappsUI = {
|
||||
let mainAction = {
|
||||
label: bundle.getString("webapps.install"),
|
||||
accessKey: bundle.getString("webapps.install.accesskey"),
|
||||
callback: function() {
|
||||
callback: () => {
|
||||
let manifestURL = aData.app.manifestURL;
|
||||
if (aData.app.manifest && aData.app.manifest.appcache_path) {
|
||||
this.downloads[manifestURL] = Promise.defer();
|
||||
}
|
||||
|
||||
let app = WebappsInstaller.init(aData);
|
||||
|
||||
if (app) {
|
||||
@ -113,18 +136,22 @@ this.webappsUI = {
|
||||
}
|
||||
|
||||
DOMApplicationRegistry.confirmInstall(aData, false, localDir, null,
|
||||
function (aManifest) {
|
||||
WebappsInstaller.install(aData, aManifest).then(
|
||||
function() {
|
||||
installationSuccessNotification(aData, app, chromeWin);
|
||||
},
|
||||
function(error) {
|
||||
Cu.reportError("Error installing webapp: " + error);
|
||||
(aManifest) => {
|
||||
Task.spawn(function() {
|
||||
try {
|
||||
yield WebappsInstaller.install(aData, aManifest);
|
||||
if (this.downloads[manifestURL]) {
|
||||
yield this.downloads[manifestURL].promise;
|
||||
}
|
||||
installationSuccessNotification(aData, app, bundle);
|
||||
} catch (ex) {
|
||||
Cu.reportError("Error installing webapp: " + ex);
|
||||
// TODO: Notify user that the installation has failed
|
||||
} finally {
|
||||
delete this.downloads[manifestURL];
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}.bind(this));
|
||||
});
|
||||
} else {
|
||||
DOMApplicationRegistry.denyInstall(aData);
|
||||
}
|
||||
@ -151,7 +178,7 @@ this.webappsUI = {
|
||||
}
|
||||
}
|
||||
|
||||
function installationSuccessNotification(aData, app, aWindow) {
|
||||
function installationSuccessNotification(aData, app, aBundle) {
|
||||
let launcher = {
|
||||
observe: function(aSubject, aTopic) {
|
||||
if (aTopic == "alertclickcallback") {
|
||||
@ -160,19 +187,13 @@ function installationSuccessNotification(aData, app, aWindow) {
|
||||
}
|
||||
};
|
||||
|
||||
let bundle = aWindow.gNavigatorBundle;
|
||||
try {
|
||||
let notifier = Cc["@mozilla.org/alerts-service;1"].
|
||||
getService(Ci.nsIAlertsService);
|
||||
|
||||
if (("@mozilla.org/alerts-service;1" in Cc)) {
|
||||
let notifier;
|
||||
try {
|
||||
notifier = Cc["@mozilla.org/alerts-service;1"].
|
||||
getService(Ci.nsIAlertsService);
|
||||
|
||||
notifier.showAlertNotification(app.iconURI.spec,
|
||||
bundle.getString("webapps.install.success"),
|
||||
app.appNameAsFilename,
|
||||
true, null, launcher);
|
||||
|
||||
} catch (ex) {}
|
||||
}
|
||||
notifier.showAlertNotification(app.iconURI.spec,
|
||||
aBundle.getString("webapps.install.success"),
|
||||
app.appNameAsFilename,
|
||||
true, null, launcher);
|
||||
} catch (ex) {}
|
||||
}
|
||||
|
@ -297,7 +297,7 @@ case "$target" in
|
||||
# The build tools got moved around to different directories in
|
||||
# SDK Tools r22. Try to locate them.
|
||||
android_build_tools=""
|
||||
for suffix in 18.0.1 18.0.0 17.0.0 android-4.2.2; do
|
||||
for suffix in android-4.3 18.0.1 18.0.0 17.0.0 android-4.2.2; do
|
||||
tools_directory="$android_sdk/../../build-tools/$suffix"
|
||||
if test -d "$tools_directory" ; then
|
||||
android_build_tools="$tools_directory"
|
||||
|
@ -43,6 +43,7 @@ function getInstalledReturnsNothing(next) {
|
||||
|
||||
function install(next) {
|
||||
var beforehand = Date.now();
|
||||
const fuzzySpan = 250;
|
||||
|
||||
confirmNextInstall();
|
||||
navigator.mozApps.install(url, null).onsuccess = function onInstall() {
|
||||
@ -50,9 +51,8 @@ function install(next) {
|
||||
|
||||
is(app.origin, "http://test", "origin");
|
||||
is(app.installOrigin, "chrome://mochitests", "install origin");
|
||||
ok(app.installTime >= beforehand, "install time (" + app.installTime + ") is after install() call (" + beforehand + ")");
|
||||
let installSuccess = Date.now();
|
||||
ok(app.installTime <= installSuccess, "install time (" + app.installTime + ") is before install success (" + installSuccess + ")");
|
||||
ok(app.installTime + fuzzySpan >= beforehand, "install time is after install() call");
|
||||
ok(app.installTime <= Date.now() + fuzzySpan, "install time is before install success");
|
||||
is(app.manifestURL, url, "manifest URL");
|
||||
is(app.manifest.name, "Basic App", "manifest.name");
|
||||
is(app.manifest.installs_allowed_from, "*",
|
||||
|
@ -43,6 +43,7 @@ function getInstalledReturnsNothing(next) {
|
||||
|
||||
function install(next) {
|
||||
var beforehand = Date.now();
|
||||
const fuzzySpan = 250;
|
||||
|
||||
confirmNextInstall();
|
||||
navigator.mozApps.install(url, null).onsuccess = function onInstall() {
|
||||
@ -50,8 +51,8 @@ function install(next) {
|
||||
|
||||
is(app.origin, "http://test", "origin");
|
||||
is(app.installOrigin, "chrome://mochitests", "install origin");
|
||||
ok(app.installTime >= beforehand, "install time is after install() call");
|
||||
ok(app.installTime <= Date.now(), "install time is before install success");
|
||||
ok(app.installTime + fuzzySpan >= beforehand, "install time is after install() call");
|
||||
ok(app.installTime <= Date.now() + fuzzySpan, "install time is before install success");
|
||||
is(app.manifestURL, url, "manifest URL");
|
||||
is(app.manifest.name, "Basic App with Launch Paths", "manifest.name");
|
||||
is(app.manifest.installs_allowed_from, "*",
|
||||
|
@ -297,7 +297,7 @@ case "$target" in
|
||||
# The build tools got moved around to different directories in
|
||||
# SDK Tools r22. Try to locate them.
|
||||
android_build_tools=""
|
||||
for suffix in 18.0.1 18.0.0 17.0.0 android-4.2.2; do
|
||||
for suffix in android-4.3 18.0.1 18.0.0 17.0.0 android-4.2.2; do
|
||||
tools_directory="$android_sdk/../../build-tools/$suffix"
|
||||
if test -d "$tools_directory" ; then
|
||||
android_build_tools="$tools_directory"
|
||||
|
@ -36,6 +36,8 @@
|
||||
#include "nsDOMDataChannelDeclarations.h"
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsDOMJSUtils.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIScriptError.h"
|
||||
@ -245,6 +247,9 @@ public:
|
||||
// providing non-fatal warnings.
|
||||
mPC->ClearSdpParseErrorMessages();
|
||||
mObserver->OnSetRemoteDescriptionSuccess();
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
mPC->setStartTime();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case SETLOCALDESCERROR:
|
||||
@ -1326,6 +1331,14 @@ PeerConnectionImpl::ShutdownMedia()
|
||||
if (!mMedia)
|
||||
return;
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
// End of call to be recorded in Telemetry
|
||||
if (!mStartTime.IsNull()){
|
||||
mozilla::TimeDuration timeDelta = mozilla::TimeStamp::Now() - mStartTime;
|
||||
Telemetry::Accumulate(Telemetry::WEBRTC_CALL_DURATION, timeDelta.ToSeconds());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Forget the reference so that we can transfer it to
|
||||
// SelfDestruct().
|
||||
mMedia.forget().get()->SelfDestruct();
|
||||
@ -1543,6 +1556,13 @@ PeerConnectionImpl::GetSdpParseErrors() {
|
||||
return mSDPParseErrorMessages;
|
||||
}
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
//Telemetry set start time
|
||||
void
|
||||
PeerConnectionImpl::setStartTime() {
|
||||
mStartTime = mozilla::TimeStamp::Now();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
static nsresult
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "PeerConnectionMedia.h"
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/net/DataChannel.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "VideoSegment.h"
|
||||
@ -281,6 +282,11 @@ public:
|
||||
// Sets the RTC Signaling State
|
||||
void SetSignalingState_m(SignalingState aSignalingState);
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
// Set start time for Telemetry
|
||||
void setStartTime();
|
||||
#endif
|
||||
|
||||
private:
|
||||
PeerConnectionImpl(const PeerConnectionImpl&rhs);
|
||||
PeerConnectionImpl& operator=(PeerConnectionImpl);
|
||||
@ -366,6 +372,11 @@ private:
|
||||
|
||||
nsRefPtr<PeerConnectionMedia> mMedia;
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
// Start time of call used for Telemetry
|
||||
mozilla::TimeStamp mStartTime;
|
||||
#endif
|
||||
|
||||
// Temporary: used to prevent multiple audio streams or multiple video streams
|
||||
// in a single PC. This is tied up in the IETF discussion around proper
|
||||
// representation of multiple streams in SDP, and strongly related to
|
||||
|
@ -1195,6 +1195,7 @@ abstract public class GeckoApp
|
||||
}
|
||||
}
|
||||
|
||||
GeckoProfile.setAsDefault(this, getProfile().getDir());
|
||||
BrowserDB.initialize(getProfile().getName());
|
||||
((GeckoApplication)getApplication()).initialize();
|
||||
|
||||
|
@ -30,10 +30,10 @@ public final class GeckoProfile {
|
||||
|
||||
private static HashMap<String, GeckoProfile> sProfileCache = new HashMap<String, GeckoProfile>();
|
||||
private static String sDefaultProfileName = null;
|
||||
private static File mMozDir;
|
||||
|
||||
private final Context mContext;
|
||||
private final String mName;
|
||||
private File mMozDir;
|
||||
private File mDir;
|
||||
|
||||
// Constants to cache whether or not a profile is "locked".
|
||||
@ -52,12 +52,8 @@ public final class GeckoProfile {
|
||||
private boolean mInGuestMode = false;
|
||||
private static GeckoProfile mGuestProfile = null;
|
||||
|
||||
static private INIParser getProfilesINI(Context context) {
|
||||
File filesDir = context.getFilesDir();
|
||||
File mozillaDir = new File(filesDir, "mozilla");
|
||||
File profilesIni = new File(mozillaDir, "profiles.ini");
|
||||
return new INIParser(profilesIni);
|
||||
}
|
||||
private static native int createSymLink(String filePath, String linkPath);
|
||||
private static native int removeSymLink(String linkPath);
|
||||
|
||||
public static GeckoProfile get(Context context) {
|
||||
if (context instanceof GeckoApp) {
|
||||
@ -123,6 +119,10 @@ public final class GeckoProfile {
|
||||
}
|
||||
|
||||
public static File ensureMozillaDirectory(Context context) throws IOException {
|
||||
if (mMozDir != null) {
|
||||
return mMozDir;
|
||||
}
|
||||
|
||||
synchronized (context) {
|
||||
File filesDir = context.getFilesDir();
|
||||
File mozDir = new File(filesDir, "mozilla");
|
||||
@ -131,6 +131,7 @@ public final class GeckoProfile {
|
||||
throw new IOException("Unable to create mozilla directory at " + mozDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
mMozDir = mozDir;
|
||||
return mozDir;
|
||||
}
|
||||
}
|
||||
@ -409,52 +410,14 @@ public final class GeckoProfile {
|
||||
mDir = findProfileDir(mozillaDir);
|
||||
if (mDir == null) {
|
||||
return false;
|
||||
} else {
|
||||
delete(mDir);
|
||||
return true;
|
||||
}
|
||||
|
||||
INIParser parser = getProfilesINI(mContext);
|
||||
|
||||
Hashtable<String, INISection> sections = parser.getSections();
|
||||
for (Enumeration<INISection> e = sections.elements(); e.hasMoreElements();) {
|
||||
INISection section = e.nextElement();
|
||||
String name = section.getStringProperty("Name");
|
||||
|
||||
if (name == null || !name.equals(mName))
|
||||
continue;
|
||||
|
||||
if (section.getName().startsWith("Profile")) {
|
||||
// ok, we have stupid Profile#-named things. Rename backwards.
|
||||
try {
|
||||
int sectionNumber = Integer.parseInt(section.getName().substring("Profile".length()));
|
||||
String curSection = "Profile" + sectionNumber;
|
||||
String nextSection = "Profile" + (sectionNumber+1);
|
||||
|
||||
sections.remove(curSection);
|
||||
|
||||
while (sections.containsKey(nextSection)) {
|
||||
parser.renameSection(nextSection, curSection);
|
||||
sectionNumber++;
|
||||
|
||||
curSection = nextSection;
|
||||
nextSection = "Profile" + (sectionNumber+1);
|
||||
}
|
||||
} catch (NumberFormatException nex) {
|
||||
// uhm, malformed Profile thing; we can't do much.
|
||||
Log.e(LOGTAG, "Malformed section name in profiles.ini: " + section.getName());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// this really shouldn't be the case, but handle it anyway
|
||||
parser.removeSection(mName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
parser.write();
|
||||
return true;
|
||||
} catch (IOException ex) {
|
||||
Log.w(LOGTAG, "Failed to remove profile " + mName + ":\n" + ex);
|
||||
return false;
|
||||
} catch (IOException ioe) {
|
||||
Log.e(LOGTAG, "Error getting profile dir", ioe);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String findDefaultProfile(Context context) {
|
||||
@ -465,91 +428,51 @@ public final class GeckoProfile {
|
||||
return sDefaultProfileName;
|
||||
}
|
||||
|
||||
// Open profiles.ini to find the correct path
|
||||
INIParser parser = getProfilesINI(context);
|
||||
|
||||
for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements();) {
|
||||
INISection section = e.nextElement();
|
||||
if (section.getIntProperty("Default") == 1) {
|
||||
sDefaultProfileName = section.getStringProperty("Name");
|
||||
return sDefaultProfileName;
|
||||
try {
|
||||
File activeDir = new File(ensureMozillaDirectory(context), "active-profile");
|
||||
if (activeDir.exists()) {
|
||||
sDefaultProfileName = activeDir.getCanonicalFile().getName();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
Log.e(LOGTAG, "Error getting currDir", ioe);
|
||||
}
|
||||
return sDefaultProfileName;
|
||||
}
|
||||
|
||||
return null;
|
||||
public static void setAsDefault(Context context, File defaultFile) {
|
||||
try {
|
||||
String symlinkPath = ensureMozillaDirectory(context).getAbsolutePath() + "/active-profile";
|
||||
File activeFile = new File(symlinkPath);
|
||||
// Unlink from previous file
|
||||
if (activeFile.exists()) {
|
||||
removeSymLink(symlinkPath);
|
||||
}
|
||||
|
||||
// Set as active profile
|
||||
createSymLink(defaultFile.getAbsolutePath(), symlinkPath);
|
||||
} catch (IOException ioe) {
|
||||
Log.e(LOGTAG, "Error setting default profile", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
private File findProfileDir(File mozillaDir) {
|
||||
// Open profiles.ini to find the correct path
|
||||
INIParser parser = getProfilesINI(mContext);
|
||||
|
||||
for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements();) {
|
||||
INISection section = e.nextElement();
|
||||
String name = section.getStringProperty("Name");
|
||||
if (name != null && name.equals(mName)) {
|
||||
if (section.getIntProperty("IsRelative") == 1) {
|
||||
return new File(mozillaDir, section.getStringProperty("Path"));
|
||||
}
|
||||
return new File(section.getStringProperty("Path"));
|
||||
}
|
||||
File nameDir = new File(mozillaDir, mName);
|
||||
if (! nameDir.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String saltProfileName(String name) {
|
||||
String allowedChars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
StringBuilder salt = new StringBuilder(16);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
salt.append(allowedChars.charAt((int)(Math.random() * allowedChars.length())));
|
||||
}
|
||||
salt.append('.');
|
||||
salt.append(name);
|
||||
return salt.toString();
|
||||
return nameDir;
|
||||
}
|
||||
|
||||
private File createProfileDir(File mozillaDir) throws IOException {
|
||||
INIParser parser = getProfilesINI(mContext);
|
||||
File profileDir = new File(mozillaDir, mName);
|
||||
|
||||
// Salt the name of our requested profile
|
||||
String saltedName = saltProfileName(mName);
|
||||
File profileDir = new File(mozillaDir, saltedName);
|
||||
while (profileDir.exists()) {
|
||||
saltedName = saltProfileName(mName);
|
||||
profileDir = new File(mozillaDir, saltedName);
|
||||
}
|
||||
|
||||
// Attempt to create the salted profile dir
|
||||
// Attempt to create the profile dir
|
||||
if (! profileDir.mkdirs()) {
|
||||
throw new IOException("Unable to create profile at " + profileDir.getAbsolutePath());
|
||||
}
|
||||
Log.d(LOGTAG, "Created new profile dir at " + profileDir.getAbsolutePath());
|
||||
|
||||
// Now update profiles.ini
|
||||
// If this is the first time its created, we also add a General section
|
||||
// look for the first profile number that isn't taken yet
|
||||
int profileNum = 0;
|
||||
while (parser.getSection("Profile" + profileNum) != null) {
|
||||
profileNum++;
|
||||
}
|
||||
|
||||
INISection profileSection = new INISection("Profile" + profileNum);
|
||||
profileSection.setProperty("Name", mName);
|
||||
profileSection.setProperty("IsRelative", 1);
|
||||
profileSection.setProperty("Path", saltedName);
|
||||
|
||||
if (parser.getSection("General") == null) {
|
||||
INISection generalSection = new INISection("General");
|
||||
generalSection.setProperty("StartWithLastProfile", 1);
|
||||
parser.addSection(generalSection);
|
||||
|
||||
// only set as default if this is the first profile we're creating
|
||||
profileSection.setProperty("Default", 1);
|
||||
}
|
||||
|
||||
parser.addSection(profileSection);
|
||||
parser.write();
|
||||
|
||||
// Write out profile creation time, mirroring the logic in nsToolkitProfileService.
|
||||
try {
|
||||
FileOutputStream stream = new FileOutputStream(profileDir.getAbsolutePath() + File.separator + "times.json");
|
||||
|
@ -139,7 +139,9 @@ public class GeckoThread extends Thread implements GeckoEventListener {
|
||||
String profile = "";
|
||||
String guest = "";
|
||||
if (GeckoAppShell.getGeckoInterface() != null) {
|
||||
if (GeckoAppShell.getGeckoInterface().getProfile().inGuestMode()) {
|
||||
if (GeckoApp.sIsUsingCustomProfile) {
|
||||
profile = " -P " + GeckoAppShell.getGeckoInterface().getProfile().getName();
|
||||
} else {
|
||||
try {
|
||||
profile = " -profile " + GeckoAppShell.getGeckoInterface().getProfile().getDir().getCanonicalPath();
|
||||
} catch (IOException ioe) { Log.e(LOGTAG, "error getting guest profile path", ioe); }
|
||||
@ -147,8 +149,6 @@ public class GeckoThread extends Thread implements GeckoEventListener {
|
||||
if (args == null || !args.contains(BrowserApp.GUEST_BROWSING_ARG)) {
|
||||
guest = " " + BrowserApp.GUEST_BROWSING_ARG;
|
||||
}
|
||||
} else if (GeckoApp.sIsUsingCustomProfile) {
|
||||
profile = " -P " + GeckoAppShell.getGeckoInterface().getProfile().getName();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,6 +147,18 @@ public class BookmarksListView extends HomeListView
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
// Adjust the item position to account for the parent folder row that is inserted
|
||||
// at the top of the list when viewing the contents of a folder.
|
||||
final BookmarksListAdapter adapter = getBookmarksListAdapter();
|
||||
if (adapter.isShowingChildFolder()) {
|
||||
position--;
|
||||
}
|
||||
|
||||
return super.onItemLongClick(parent, view, position, id);
|
||||
}
|
||||
|
||||
private BookmarksListAdapter getBookmarksListAdapter() {
|
||||
BookmarksListAdapter adapter;
|
||||
ListAdapter listAdapter = getAdapter();
|
||||
|
@ -26,7 +26,6 @@ import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
@ -46,9 +45,6 @@ abstract class HomeFragment extends Fragment {
|
||||
// Share MIME type.
|
||||
private static final String SHARE_MIME_TYPE = "text/plain";
|
||||
|
||||
// URL to Title replacement regex.
|
||||
private static final String REGEX_URL_TO_TITLE = "^([a-z]+://)?(www\\.)?";
|
||||
|
||||
// Whether the fragment can load its content or not
|
||||
// This is used to defer data loading until the editing
|
||||
// mode animation ends.
|
||||
@ -87,7 +83,7 @@ abstract class HomeFragment extends Fragment {
|
||||
MenuInflater inflater = new MenuInflater(view.getContext());
|
||||
inflater.inflate(R.menu.home_contextmenu, menu);
|
||||
|
||||
menu.setHeaderTitle(info.title);
|
||||
menu.setHeaderTitle(info.getDisplayTitle());
|
||||
|
||||
// Hide the "Edit" menuitem if this item isn't a bookmark.
|
||||
if (info.bookmarkId < 0) {
|
||||
@ -126,7 +122,7 @@ abstract class HomeFragment extends Fragment {
|
||||
Log.e(LOGTAG, "Can't share because URL is null");
|
||||
} else {
|
||||
GeckoAppShell.openUriExternal(info.url, SHARE_MIME_TYPE, "", "",
|
||||
Intent.ACTION_SEND, info.title);
|
||||
Intent.ACTION_SEND, info.getDisplayTitle());
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,8 +132,7 @@ abstract class HomeFragment extends Fragment {
|
||||
return false;
|
||||
}
|
||||
|
||||
String shortcutTitle = TextUtils.isEmpty(info.title) ? info.url.replaceAll(REGEX_URL_TO_TITLE, "") : info.title;
|
||||
new AddToLauncherTask(info.url, shortcutTitle).execute();
|
||||
new AddToLauncherTask(info.url, info.getDisplayTitle()).execute();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.MotionEvent;
|
||||
@ -110,6 +111,10 @@ public class HomeListView extends ListView
|
||||
* A ContextMenuInfo for HomeListView that adds details from the cursor.
|
||||
*/
|
||||
public static class HomeContextMenuInfo extends AdapterContextMenuInfo {
|
||||
|
||||
// URL to Title replacement regex.
|
||||
private static final String REGEX_URL_TO_TITLE = "^([a-z]+://)?(www\\.)?";
|
||||
|
||||
public int bookmarkId;
|
||||
public int historyId;
|
||||
public String url;
|
||||
@ -179,5 +184,9 @@ public class HomeListView extends ListView
|
||||
display = Combined.DISPLAY_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
public String getDisplayTitle() {
|
||||
return TextUtils.isEmpty(title) ? url.replaceAll(REGEX_URL_TO_TITLE, "") : title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewStub;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
|
@ -283,6 +283,9 @@ size. -->
|
||||
The "%I" in the string will be replaced by a small image of the icon described, and can be moved to wherever
|
||||
it is applicable. Please keep the spacing around the "%I" string. -->
|
||||
<!ENTITY home_reading_list_hint "TIP: Save articles to your reading list by long pressing the %I icon when it appears in the title bar.">
|
||||
<!-- Localization note (home_reading_list_hint_accessible): This string is used
|
||||
as alternate text for accessibility. It is not visible in the UI. -->
|
||||
<!ENTITY home_reading_list_hint_accessible "TIP: Save articles to your reading list by long pressing the reader mode button when it appears in the title bar.">
|
||||
|
||||
<!ENTITY home_most_visited_empty "Websites you visit most frequently show up here.">
|
||||
<!ENTITY pin_bookmark_dialog_hint "Enter a search keyword">
|
||||
|
@ -14,17 +14,21 @@
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<TextView android:id="@+id/home_empty_text"
|
||||
android:layout_width="fill_parent"
|
||||
<ImageView android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="top|center"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/icon_reading_list_empty"
|
||||
android:paddingBottom="10dp"/>
|
||||
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="0dip"
|
||||
android:gravity="top|center"
|
||||
android:text="@string/home_reading_list_empty"
|
||||
android:textAppearance="@style/TextAppearance.EmptyMessage"
|
||||
android:drawableTop="@drawable/icon_reading_list_empty"
|
||||
android:drawablePadding="10dp"
|
||||
android:paddingLeft="50dp"
|
||||
android:paddingRight="50dp"
|
||||
android:layout_weight="4"/>
|
||||
android:layout_weight="3"/>
|
||||
|
||||
<ImageView android:src="@drawable/divider_horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
@ -38,6 +42,7 @@
|
||||
android:gravity="bottom"
|
||||
android:text="@string/home_reading_list_hint"
|
||||
android:textAppearance="@style/TextAppearance.Small"
|
||||
android:contentDescription="@string/home_reading_list_hint_accessible"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingBottom="15dp"
|
||||
android:paddingLeft="25dp"
|
||||
|
@ -11,7 +11,6 @@
|
||||
<dimen name="tabs_counter_size">26sp</dimen>
|
||||
<dimen name="tabs_panel_indicator_width">60dp</dimen>
|
||||
<dimen name="tabs_panel_list_padding">8dip</dimen>
|
||||
<dimen name="url_bar_offset_left">84dip</dimen>
|
||||
<dimen name="history_tab_widget_width">270dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
@ -262,6 +262,7 @@
|
||||
<string name="home_most_visited_empty">&home_most_visited_empty;</string>
|
||||
<string name="home_reading_list_empty">&home_reading_list_empty;</string>
|
||||
<string name="home_reading_list_hint">&home_reading_list_hint;</string>
|
||||
<string name="home_reading_list_hint_accessible">&home_reading_list_hint_accessible;</string>
|
||||
<string name="pin_bookmark_dialog_hint">&pin_bookmark_dialog_hint;</string>
|
||||
|
||||
<string name="filepicker_title">&filepicker_title;</string>
|
||||
|
@ -48,6 +48,7 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
|
||||
private static final int VERIFY_URL_TIMEOUT = 2000;
|
||||
private static final int MAX_LIST_ATTEMPTS = 3;
|
||||
private static final int MAX_WAIT_ENABLED_TEXT_MS = 10000;
|
||||
private static final int MAX_WAIT_HOME_PAGER_HIDDEN_MS = 10000;
|
||||
public static final int MAX_WAIT_MS = 3000;
|
||||
|
||||
// Note: DEFAULT_BOOKMARKS_TITLES.length == DEFAULT_BOOKMARKS_URLS.length
|
||||
@ -483,6 +484,22 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
|
||||
}
|
||||
}
|
||||
|
||||
public final void verifyHomePagerHidden() {
|
||||
final Element homePagerElement = mDriver.findElement(mActivity, "home_pager");
|
||||
final View homePagerView = mActivity.findViewById(homePagerElement.getId());
|
||||
|
||||
boolean rc = waitForTest(new BooleanTest() {
|
||||
@Override
|
||||
public boolean test() {
|
||||
return homePagerView.getVisibility() != View.VISIBLE;
|
||||
}
|
||||
}, MAX_WAIT_HOME_PAGER_HIDDEN_MS);
|
||||
|
||||
if (!rc) {
|
||||
mAsserter.ok(rc, "Verify HomePager is hidden", "HomePager is hidden");
|
||||
}
|
||||
}
|
||||
|
||||
public final void verifyPageTitle(String title) {
|
||||
Element urlBarTitle = mDriver.findElement(getActivity(), URL_BAR_TITLE_ID);
|
||||
String pageTitle = null;
|
||||
|
@ -12,6 +12,8 @@ public class testCheck2 extends PixelTest {
|
||||
|
||||
blockForGeckoReady();
|
||||
loadAndPaint(url);
|
||||
verifyHomePagerHidden();
|
||||
|
||||
mDriver.setupScrollHandling();
|
||||
|
||||
/*
|
||||
|
@ -468,7 +468,12 @@ var SelectionHandler = {
|
||||
let selectedText = this._getSelectedText();
|
||||
if (selectedText.length) {
|
||||
let req = Services.search.defaultEngine.getSubmission(selectedText);
|
||||
BrowserApp.selectOrOpenTab(req.uri.spec);
|
||||
let parent = BrowserApp.selectedTab;
|
||||
let isPrivate = PrivateBrowsingUtils.isWindowPrivate(parent.browser.contentWindow);
|
||||
// Set current tab as parent of new tab, and set new tab as private if the parent is.
|
||||
BrowserApp.addTab(req.uri.spec, {parentId: parent.id,
|
||||
selected: true,
|
||||
isPrivate: isPrivate});
|
||||
}
|
||||
this._closeSelection();
|
||||
},
|
||||
|
@ -10,6 +10,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/PluralForm.jsm");
|
||||
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
|
||||
let gStrings = Services.strings.createBundle("chrome://browser/locale/aboutDownloads.properties");
|
||||
|
||||
let downloadTemplate =
|
||||
@ -460,20 +462,15 @@ let Downloads = {
|
||||
|
||||
removeDownload: function dl_removeDownload(aItem) {
|
||||
this._getDownloadForElement(aItem, function(aDownload) {
|
||||
let f = null;
|
||||
try {
|
||||
f = aDownload.targetFile;
|
||||
} catch (ex) {
|
||||
// even if there is no file, pretend that there is so that we can remove
|
||||
// it from the list
|
||||
f = { leafName: "" };
|
||||
if (aDownload.targetFile) {
|
||||
OS.File.remove(aDownload.targetFile.path).then(null, function onError(reason) {
|
||||
if (!(reason instanceof OS.File.Error && reason.becauseNoSuchFile)) {
|
||||
this.logError("removeDownload() " + reason, aDownload);
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
aDownload.remove();
|
||||
try {
|
||||
if (f) f.remove(false);
|
||||
} catch (ex) {
|
||||
this.logError("removeDownload() " + ex, aDownload);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
@ -531,17 +528,15 @@ let Downloads = {
|
||||
|
||||
cancelDownload: function dl_cancelDownload(aItem) {
|
||||
this._getDownloadForElement(aItem, function(aDownload) {
|
||||
try {
|
||||
aDownload.cancel();
|
||||
let f = aDownload.targetFile;
|
||||
OS.File.remove(aDownload.targetFile.path).then(null, function onError(reason) {
|
||||
if (!(reason instanceof OS.File.Error && reason.becauseNoSuchFile)) {
|
||||
this.logError("cancelDownload() " + reason, aDownload);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
if (f.exists())
|
||||
f.remove(false);
|
||||
aDownload.cancel();
|
||||
|
||||
this._updateDownloadRow(aItem, aDownload);
|
||||
} catch (ex) {
|
||||
this.logError("cancelDownload() " + ex, aDownload);
|
||||
}
|
||||
this._updateDownloadRow(aItem, aDownload);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
|
@ -35,8 +35,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "UserAgentOverrides",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
|
||||
"resource://gre/modules/LoginManagerContent.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
|
||||
@ -2862,6 +2862,7 @@ Tab.prototype = {
|
||||
this.browser.focus();
|
||||
this.browser.docShellIsActive = true;
|
||||
Reader.updatePageAction(this);
|
||||
HelperApps.updatePageAction(this.browser.currentURI);
|
||||
} else {
|
||||
this.browser.setAttribute("type", "content-targetable");
|
||||
this.browser.docShellIsActive = false;
|
||||
@ -6514,13 +6515,15 @@ var SearchEngines = {
|
||||
|
||||
let suggestTemplate = null;
|
||||
let suggestEngine = null;
|
||||
let engine = this.getSuggestionEngine();
|
||||
if (engine != null) {
|
||||
|
||||
// Check to see if the default engine supports search suggestions. We only need to check
|
||||
// the default engine because we only show suggestions for the default engine in the UI.
|
||||
let engine = Services.search.defaultEngine;
|
||||
if (engine.supportsResponseType("application/x-suggestions+json")) {
|
||||
suggestEngine = engine.name;
|
||||
suggestTemplate = engine.getSubmission("__searchTerms__", "application/x-suggestions+json").uri.spec;
|
||||
}
|
||||
|
||||
|
||||
// By convention, the currently configured default engine is at position zero in searchEngines.
|
||||
sendMessageToJava({
|
||||
type: "SearchEngines:Data",
|
||||
@ -6576,19 +6579,6 @@ var SearchEngines = {
|
||||
}
|
||||
},
|
||||
|
||||
getSuggestionEngine: function () {
|
||||
let engines = [ Services.search.currentEngine,
|
||||
Services.search.defaultEngine ];
|
||||
|
||||
for (let i = 0; i < engines.length; i++) {
|
||||
let engine = engines[i];
|
||||
if (engine && engine.supportsResponseType("application/x-suggestions+json"))
|
||||
return engine;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
addEngine: function addEngine(aElement) {
|
||||
let form = aElement.form;
|
||||
let charset = aElement.ownerDocument.characterSet;
|
||||
@ -6907,18 +6897,9 @@ var WebappsUI = {
|
||||
|
||||
_writeData: function(aFile, aPrefs) {
|
||||
if (aPrefs.length > 0) {
|
||||
let data = JSON.stringify(aPrefs);
|
||||
|
||||
let ostream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
|
||||
ostream.init(aFile, -1, -1, 0);
|
||||
|
||||
let istream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
|
||||
istream.setData(data, data.length);
|
||||
|
||||
NetUtil.asyncCopy(istream, ostream, function(aResult) {
|
||||
if (!Components.isSuccessCode(aResult)) {
|
||||
console.log("Error writing default prefs: " + aResult);
|
||||
}
|
||||
let array = new TextEncoder().encode(JSON.stringify(aPrefs));
|
||||
OS.File.writeAtomic(aFile.path, array, { tmpPath: aFile.path + ".tmp" }).then(null, function onError(reason) {
|
||||
console.log("Error writing default prefs: " + reason);
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -7738,16 +7719,9 @@ var Distribution = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the data for the later sessions
|
||||
let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
|
||||
ostream.init(this._file, 0x02 | 0x08 | 0x20, parseInt("600", 8), ostream.DEFER_OPEN);
|
||||
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
|
||||
// Asynchronously copy the data to the file.
|
||||
let istream = converter.convertToInputStream(aData);
|
||||
NetUtil.asyncCopy(istream, ostream, function(rc) { });
|
||||
let array = new TextEncoder().encode(aData);
|
||||
OS.File.writeAtomic(this._file.path, array, { tmpPath: this._file.path + ".tmp" });
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -7837,25 +7811,19 @@ var Distribution = {
|
||||
// aFile is an nsIFile
|
||||
// aCallback takes the parsed JSON object as a parameter
|
||||
readJSON: function dc_readJSON(aFile, aCallback) {
|
||||
if (!aFile.exists())
|
||||
return;
|
||||
|
||||
let channel = NetUtil.newChannel(aFile);
|
||||
channel.contentType = "application/json";
|
||||
NetUtil.asyncFetch(channel, function(aStream, aResult) {
|
||||
if (!Components.isSuccessCode(aResult)) {
|
||||
Cu.reportError("Distribution: Could not read from " + aFile.leafName + " file");
|
||||
return;
|
||||
}
|
||||
|
||||
let raw = NetUtil.readInputStreamToString(aStream, aStream.available(), { charset : "UTF-8" }) || "";
|
||||
aStream.close();
|
||||
Task.spawn(function() {
|
||||
let bytes = yield OS.File.read(aFile.path);
|
||||
let raw = new TextDecoder().decode(bytes) || "";
|
||||
|
||||
try {
|
||||
aCallback(JSON.parse(raw));
|
||||
} catch (e) {
|
||||
Cu.reportError("Distribution: Could not parse JSON: " + e);
|
||||
}
|
||||
}).then(null, function onError(reason) {
|
||||
if (!(reason instanceof OS.File.Error && reason.becauseNoSuchFile)) {
|
||||
Cu.reportError("Distribution: Could not read from " + aFile.leafName + " file");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -11,6 +11,8 @@ function dump(a) {
|
||||
|
||||
const URI_GENERIC_ICON_DOWNLOAD = "drawable://alert_download";
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
|
||||
var Downloads = {
|
||||
_initialized: false,
|
||||
_dlmgr: null,
|
||||
@ -54,8 +56,8 @@ var Downloads = {
|
||||
|
||||
let fileURI = aDownload.target.spec;
|
||||
let f = this._getLocalFile(fileURI);
|
||||
if (f.exists())
|
||||
f.remove(false);
|
||||
|
||||
OS.File.remove(f.path);
|
||||
},
|
||||
|
||||
showAlert: function dl_showAlert(aDownload, aMessage, aTitle, aIcon) {
|
||||
|
@ -15,8 +15,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
|
||||
"resource://gre/modules/AddonRepository.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
|
||||
function getPref(func, preference, defaultValue) {
|
||||
try {
|
||||
@ -130,19 +129,10 @@ var RecommendedSearchResults = {
|
||||
if (!aData)
|
||||
return;
|
||||
|
||||
// Initialize the file output stream.
|
||||
let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
|
||||
ostream.init(aFile, 0x02 | 0x08 | 0x20, 0600, ostream.DEFER_OPEN);
|
||||
|
||||
// Obtain a converter to convert our data to a UTF-8 encoded input stream.
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
|
||||
// Asynchronously copy the data to the file.
|
||||
let istream = converter.convertToInputStream(aData);
|
||||
NetUtil.asyncCopy(istream, ostream, function(rc) {
|
||||
if (Components.isSuccessCode(rc))
|
||||
Services.obs.notifyObservers(null, "recommended-addons-cache-updated", "");
|
||||
let array = new TextEncoder().encode(aData);
|
||||
OS.File.writeAtomic(aFile.path, array, { tmpPath: aFile.path + ".tmp" }).then(function onSuccess() {
|
||||
Services.obs.notifyObservers(null, "recommended-addons-cache-updated", "");
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -133,10 +133,9 @@ HelperAppLauncherDialog.prototype = {
|
||||
// Remove the file so that it's not there when we ensure non-existence later;
|
||||
// this is safe because for the file to exist, the user would have had to
|
||||
// confirm that he wanted the file overwritten.
|
||||
if (file.exists())
|
||||
file.remove(false);
|
||||
file.remove(false);
|
||||
}
|
||||
catch (e) { }
|
||||
catch (e) {}
|
||||
var newDir = file.parent.QueryInterface(Ci.nsILocalFile);
|
||||
prefs.setComplexValue("browser.download.lastDir", Ci.nsILocalFile, newDir);
|
||||
file = this.validateLeafName(newDir, file.leafName, null);
|
||||
|
@ -15,8 +15,8 @@ XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
|
||||
"@mozilla.org/xre/app-info;1", "nsICrashReporter");
|
||||
#endif
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
|
||||
function dump(a) {
|
||||
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(a);
|
||||
@ -66,17 +66,8 @@ SessionStore.prototype = {
|
||||
},
|
||||
|
||||
_clearDisk: function ss_clearDisk() {
|
||||
if (this._sessionFile.exists()) {
|
||||
try {
|
||||
this._sessionFile.remove(false);
|
||||
} catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
|
||||
}
|
||||
if (this._sessionFileBackup.exists()) {
|
||||
try {
|
||||
this._sessionFileBackup.remove(false);
|
||||
} catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
|
||||
}
|
||||
|
||||
OS.File.remove(this._sessionFile.path);
|
||||
OS.File.remove(this._sessionFileBackup.path);
|
||||
},
|
||||
|
||||
_sendMessageToJava: function (aMsg) {
|
||||
@ -155,8 +146,11 @@ SessionStore.prototype = {
|
||||
// Move this session to sessionstore.bak so that:
|
||||
// 1) we can get "tabs from last time" from sessionstore.bak
|
||||
// 2) if sessionstore.js exists on next start, we know we crashed
|
||||
if (this._sessionFile.exists())
|
||||
this._sessionFile.moveTo(null, this._sessionFileBackup.leafName);
|
||||
OS.File.move(this._sessionFile.path, this._sessionFileBackup.path).then(null, function onError(reason) {
|
||||
if (!(reason instanceof OS.File.Error && reason.becauseNoSuchFile)) {
|
||||
Cu.reportError("Error moving sessionstore files: " + reason);
|
||||
}
|
||||
});
|
||||
|
||||
observerService.removeObserver(this, "domwindowopened");
|
||||
observerService.removeObserver(this, "domwindowclosed");
|
||||
@ -564,20 +558,10 @@ SessionStore.prototype = {
|
||||
if (!stateString.data)
|
||||
return;
|
||||
|
||||
// Initialize the file output stream.
|
||||
let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
|
||||
ostream.init(aFile, 0x02 | 0x08 | 0x20, 0600, ostream.DEFER_OPEN);
|
||||
|
||||
// Obtain a converter to convert our data to a UTF-8 encoded input stream.
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
|
||||
// Asynchronously copy the data to the file.
|
||||
let istream = converter.convertToInputStream(aData);
|
||||
NetUtil.asyncCopy(istream, ostream, function(rc) {
|
||||
if (Components.isSuccessCode(rc)) {
|
||||
Services.obs.notifyObservers(null, "sessionstore-state-write-complete", "");
|
||||
}
|
||||
let array = new TextEncoder().encode(aData);
|
||||
OS.File.writeAtomic(aFile.path, array, { tmpPath: aFile.path + ".tmp" }).then(function onSuccess() {
|
||||
Services.obs.notifyObservers(null, "sessionstore-state-write-complete", "");
|
||||
});
|
||||
},
|
||||
|
||||
@ -1006,26 +990,17 @@ SessionStore.prototype = {
|
||||
// session will be read from sessionstore.bak (which is also used for
|
||||
// "tabs from last time").
|
||||
if (aSessionString == null) {
|
||||
if (!this._sessionFileBackup.exists()) {
|
||||
throw "Session file doesn't exist";
|
||||
}
|
||||
|
||||
let channel = NetUtil.newChannel(this._sessionFileBackup);
|
||||
channel.contentType = "application/json";
|
||||
NetUtil.asyncFetch(channel, function(aStream, aResult) {
|
||||
try {
|
||||
if (!Components.isSuccessCode(aResult)) {
|
||||
throw "Could not fetch session file";
|
||||
}
|
||||
|
||||
let data = NetUtil.readInputStreamToString(aStream, aStream.available(), { charset : "UTF-8" }) || "";
|
||||
aStream.close();
|
||||
|
||||
restoreWindow(data);
|
||||
} catch (e) {
|
||||
Cu.reportError("SessionStore: " + e.message);
|
||||
notifyObservers("fail");
|
||||
Task.spawn(function() {
|
||||
let bytes = yield OS.File.read(this._sessionFileBackup.path);
|
||||
let data = JSON.parse(new TextDecoder().decode(bytes) || "");
|
||||
restoreWindow(data);
|
||||
}.bind(this)).then(null, function onError(reason) {
|
||||
if (reason instanceof OS.File.Error && reason.becauseNoSuchFile) {
|
||||
Cu.reportError("Session file doesn't exist");
|
||||
} else {
|
||||
Cu.reportError("SessionStore: " + reason.message);
|
||||
}
|
||||
notifyObservers("fail");
|
||||
});
|
||||
} else {
|
||||
restoreWindow(aSessionString);
|
||||
|
@ -59,6 +59,7 @@ body {
|
||||
}
|
||||
|
||||
.domain-border {
|
||||
margin-top: 15px;
|
||||
border-bottom: 1.5px solid #777777;
|
||||
width: 50%;
|
||||
}
|
||||
|
@ -299,6 +299,36 @@ loadNSSLibs(const char *apkName)
|
||||
return setup_nss_functions(nss_handle, nspr_handle, plc_handle);
|
||||
}
|
||||
|
||||
extern "C" NS_EXPORT void JNICALL
|
||||
Java_org_mozilla_gecko_GeckoProfile_createSymLink(JNIEnv* jenv, jclass clazz, jstring inputFilePath, jstring inputSymLinkPath)
|
||||
{
|
||||
const char* inputFilePathC = jenv->GetStringUTFChars(inputFilePath, NULL);
|
||||
const char* inputSymLinkPathC = jenv->GetStringUTFChars(inputSymLinkPath, NULL);
|
||||
|
||||
int result = symlink(inputFilePathC, inputSymLinkPathC);
|
||||
|
||||
//release resources when done
|
||||
jenv->ReleaseStringUTFChars(inputFilePath, inputFilePathC);
|
||||
jenv->ReleaseStringUTFChars(inputSymLinkPath, inputSymLinkPathC);
|
||||
|
||||
if (result == -1)
|
||||
JNI_Throw(jenv, "java/io/IOException", strerror(errno));
|
||||
}
|
||||
|
||||
extern "C" NS_EXPORT void JNICALL
|
||||
Java_org_mozilla_gecko_GeckoProfile_removeSymLink(JNIEnv* jenv, jclass clazz, jstring inputSymLinkPath)
|
||||
{
|
||||
const char *inputSymLinkPathC = jenv->GetStringUTFChars(inputSymLinkPath, NULL);
|
||||
|
||||
int result = unlink(inputSymLinkPathC);
|
||||
|
||||
//release resources when done
|
||||
jenv->ReleaseStringUTFChars(inputSymLinkPath, inputSymLinkPathC);
|
||||
|
||||
if (result == -1)
|
||||
JNI_Throw(jenv, "java/io/IOException", strerror(errno));
|
||||
}
|
||||
|
||||
extern "C" NS_EXPORT void JNICALL
|
||||
Java_org_mozilla_gecko_mozglue_GeckoLoader_loadGeckoLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName)
|
||||
{
|
||||
|
@ -117,21 +117,20 @@ void
|
||||
StorageBaseStatementInternal::asyncFinalize()
|
||||
{
|
||||
nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
|
||||
if (!target) {
|
||||
// If we cannot get the background thread, we have to assume it has been
|
||||
// shutdown (or is in the process of doing so). As a result, we should
|
||||
// just finalize it here and now.
|
||||
destructorAsyncFinalize();
|
||||
}
|
||||
else {
|
||||
if (target) {
|
||||
// Attempt to finalize asynchronously
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new AsyncStatementFinalizer(this, mDBConnection);
|
||||
|
||||
// If the dispatching did not go as planned, finalize now.
|
||||
if (NS_FAILED(target->Dispatch(event, NS_DISPATCH_NORMAL))) {
|
||||
destructorAsyncFinalize();
|
||||
}
|
||||
// Dispatch. Note that dispatching can fail, typically if
|
||||
// we have a race condition with asyncClose(). It's ok,
|
||||
// let asyncClose() win.
|
||||
(void)target->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
// If we cannot get the background thread,
|
||||
// mozStorageConnection::AsyncClose() has already been called and
|
||||
// the statement either has been or will be cleaned up by
|
||||
// internalClose().
|
||||
}
|
||||
|
||||
void
|
||||
@ -140,17 +139,28 @@ StorageBaseStatementInternal::destructorAsyncFinalize()
|
||||
if (!mAsyncStatement)
|
||||
return;
|
||||
|
||||
// If we reach this point, our owner has not finalized this
|
||||
// statement, yet we are being destructed. If possible, we want to
|
||||
// auto-finalize it early, to release the resources early.
|
||||
nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget();
|
||||
if (target) {
|
||||
// If we can get the async execution target, we can indeed finalize
|
||||
// the statement, as the connection is still open.
|
||||
bool isAsyncThread = false;
|
||||
(void)target->IsOnCurrentThread(&isAsyncThread);
|
||||
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement);
|
||||
if (NS_SUCCEEDED(target->Dispatch(event, NS_DISPATCH_NORMAL))) {
|
||||
mAsyncStatement = nullptr;
|
||||
return;
|
||||
if (isAsyncThread) {
|
||||
(void)event->Run();
|
||||
} else {
|
||||
(void)target->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
}
|
||||
// (no async thread remains or we could not dispatch to it)
|
||||
(void)::sqlite3_finalize(mAsyncStatement);
|
||||
|
||||
// We might not be able to dispatch to the background thread,
|
||||
// presumably because it is being shutdown. Since said shutdown will
|
||||
// finalize the statement, we just need to clean-up around here.
|
||||
mAsyncStatement = nullptr;
|
||||
}
|
||||
|
||||
|
@ -133,12 +133,10 @@ public:
|
||||
* calling thread).
|
||||
*/
|
||||
CompletionNotifier(mozIStorageStatementCallback *aCallback,
|
||||
ExecutionState aReason,
|
||||
StatementDataArray &aStatementData)
|
||||
ExecutionState aReason)
|
||||
: mCallback(aCallback)
|
||||
, mReason(aReason)
|
||||
{
|
||||
mStatementData.SwapElements(aStatementData);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
@ -148,16 +146,10 @@ public:
|
||||
NS_RELEASE(mCallback);
|
||||
}
|
||||
|
||||
// The async thread could still hold onto a reference to us, so we need to
|
||||
// make sure we release our reference to the StatementData now in case our
|
||||
// destructor happens in a different thread.
|
||||
mStatementData.Clear();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
StatementDataArray mStatementData;
|
||||
mozIStorageStatementCallback *mCallback;
|
||||
ExecutionState mReason;
|
||||
};
|
||||
@ -428,11 +420,17 @@ AsyncExecuteStatements::notifyComplete()
|
||||
NS_ASSERTION(mState != PENDING,
|
||||
"Still in a pending state when calling Complete!");
|
||||
|
||||
// Finalize our statements before we try to commit or rollback. If we are
|
||||
// Reset our statements before we try to commit or rollback. If we are
|
||||
// canceling and have statements that think they have pending work, the
|
||||
// rollback will fail.
|
||||
for (uint32_t i = 0; i < mStatements.Length(); i++)
|
||||
mStatements[i].finalize();
|
||||
mStatements[i].reset();
|
||||
|
||||
// Release references to the statement data as soon as possible. If this
|
||||
// is the last reference, statements will be finalized immediately on the
|
||||
// async thread, hence avoiding several bounces between threads and possible
|
||||
// race conditions with AsyncClose().
|
||||
mStatements.Clear();
|
||||
|
||||
// Handle our transaction, if we have one
|
||||
if (mTransactionManager) {
|
||||
@ -454,9 +452,7 @@ AsyncExecuteStatements::notifyComplete()
|
||||
// Always generate a completion notification; it is what guarantees that our
|
||||
// destruction does not happen here on the async thread.
|
||||
nsRefPtr<CompletionNotifier> completionEvent =
|
||||
new CompletionNotifier(mCallback, mState, mStatements);
|
||||
NS_ASSERTION(mStatements.IsEmpty(),
|
||||
"Should have given up ownership of mStatements!");
|
||||
new CompletionNotifier(mCallback, mState);
|
||||
|
||||
// We no longer own mCallback (the CompletionNotifier takes ownership).
|
||||
mCallback = nullptr;
|
||||
|
@ -329,15 +329,13 @@ WaitForUnlockNotify(sqlite3* aDatabase)
|
||||
|
||||
namespace {
|
||||
|
||||
class AsyncCloseConnection : public nsRunnable
|
||||
class AsyncCloseConnection MOZ_FINAL: public nsRunnable
|
||||
{
|
||||
public:
|
||||
AsyncCloseConnection(Connection *aConnection,
|
||||
nsIEventTarget *aCallingThread,
|
||||
nsIRunnable *aCallbackEvent,
|
||||
already_AddRefed<nsIThread> aAsyncExecutionThread)
|
||||
: mConnection(aConnection)
|
||||
, mCallingThread(aCallingThread)
|
||||
, mCallbackEvent(aCallbackEvent)
|
||||
, mAsyncExecutionThread(aAsyncExecutionThread)
|
||||
{
|
||||
@ -345,46 +343,39 @@ public:
|
||||
|
||||
NS_METHOD Run()
|
||||
{
|
||||
// This event is first dispatched to the background thread to ensure that
|
||||
// all pending asynchronous events are completed, and then back to the
|
||||
// calling thread to actually close and notify.
|
||||
bool onCallingThread = false;
|
||||
(void)mCallingThread->IsOnCurrentThread(&onCallingThread);
|
||||
if (!onCallingThread) {
|
||||
#ifdef DEBUG
|
||||
{
|
||||
bool onAsyncThread = false;
|
||||
(void)mAsyncExecutionThread->IsOnCurrentThread(&onAsyncThread);
|
||||
MOZ_ASSERT(onAsyncThread);
|
||||
}
|
||||
#endif
|
||||
(void)mCallingThread->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
return NS_OK;
|
||||
}
|
||||
// This code is executed on the background thread
|
||||
bool onAsyncThread = false;
|
||||
(void)mAsyncExecutionThread->IsOnCurrentThread(&onAsyncThread);
|
||||
MOZ_ASSERT(onAsyncThread);
|
||||
#endif // DEBUG
|
||||
|
||||
// Internal close.
|
||||
(void)mConnection->internalClose();
|
||||
if (mCallbackEvent)
|
||||
(void)mCallingThread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
|
||||
|
||||
(void)mAsyncExecutionThread->Shutdown();
|
||||
|
||||
// Because we have no guarantee that the invocation of this method on the
|
||||
// asynchronous thread has fully completed (including the Release of the
|
||||
// reference to this object held by that event loop), we need to explicitly
|
||||
// null out our pointers here. It is possible this object will be destroyed
|
||||
// on the asynchronous thread and if the references are still alive we will
|
||||
// release them on that thread. We definitely do not want that for
|
||||
// mConnection and it's nice to avoid for mCallbackEvent too. We do not
|
||||
// null out mCallingThread because it is conceivable the async thread might
|
||||
// still be 'in' the object.
|
||||
mConnection = nullptr;
|
||||
mCallbackEvent = nullptr;
|
||||
// Callback
|
||||
if (mCallbackEvent) {
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
(void)NS_GetMainThread(getter_AddRefs(thread));
|
||||
(void)thread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
~AsyncCloseConnection() {
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
(void)NS_GetMainThread(getter_AddRefs(thread));
|
||||
// Handle ambiguous nsISupports inheritance.
|
||||
Connection *rawConnection = nullptr;
|
||||
mConnection.swap(rawConnection);
|
||||
(void)NS_ProxyRelease(thread,
|
||||
NS_ISUPPORTS_CAST(mozIStorageConnection *,
|
||||
rawConnection));
|
||||
(void)NS_ProxyRelease(thread, mCallbackEvent);
|
||||
}
|
||||
private:
|
||||
nsRefPtr<Connection> mConnection;
|
||||
nsCOMPtr<nsIEventTarget> mCallingThread;
|
||||
nsCOMPtr<nsIRunnable> mCallbackEvent;
|
||||
nsCOMPtr<nsIThread> mAsyncExecutionThread;
|
||||
};
|
||||
@ -819,13 +810,9 @@ Connection::internalClose()
|
||||
"Did not call setClosedState!");
|
||||
}
|
||||
|
||||
{ // Ensure that we are being called on the thread we were opened with.
|
||||
bool onOpenedThread = false;
|
||||
(void)threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
|
||||
NS_ASSERTION(onOpenedThread,
|
||||
"Not called on the thread the database was opened on!");
|
||||
}
|
||||
#endif
|
||||
bool onOpeningThread = false;
|
||||
(void)threadOpenedOn->IsOnCurrentThread(&onOpeningThread);
|
||||
#endif // DEBUG
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
nsAutoCString leafName(":memory");
|
||||
@ -835,20 +822,62 @@ Connection::internalClose()
|
||||
leafName.get()));
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
// Notify about any non-finalized statements.
|
||||
sqlite3_stmt *stmt = nullptr;
|
||||
while ((stmt = ::sqlite3_next_stmt(mDBConn, stmt))) {
|
||||
char *msg = ::PR_smprintf("SQL statement '%s' was not finalized",
|
||||
::sqlite3_sql(stmt));
|
||||
NS_WARNING(msg);
|
||||
::PR_smprintf_free(msg);
|
||||
}
|
||||
#endif
|
||||
// At this stage, we may still have statements that need to be
|
||||
// finalized. Attempt to close the database connection. This will
|
||||
// always disconnect any virtual tables and cleanly finalize their
|
||||
// internal statements. Once this is done, closing may fail due to
|
||||
// unfinalized client statements, in which case we need to finalize
|
||||
// these statements and close again.
|
||||
|
||||
int srv = ::sqlite3_close(mDBConn);
|
||||
NS_ASSERTION(srv == SQLITE_OK,
|
||||
int srv = sqlite3_close(mDBConn);
|
||||
|
||||
if (srv == SQLITE_BUSY) {
|
||||
// We still have non-finalized statements. Finalize them.
|
||||
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
while ((stmt = ::sqlite3_next_stmt(mDBConn, stmt))) {
|
||||
PR_LOG(gStorageLog, PR_LOG_NOTICE,
|
||||
("Auto-finalizing SQL statement '%s' (%x)",
|
||||
::sqlite3_sql(stmt),
|
||||
stmt));
|
||||
|
||||
#ifdef DEBUG
|
||||
char *msg = ::PR_smprintf("SQL statement '%s' (%x) should have been finalized",
|
||||
::sqlite3_sql(stmt),
|
||||
stmt);
|
||||
NS_WARNING(msg);
|
||||
::PR_smprintf_free(msg);
|
||||
#endif // DEBUG
|
||||
|
||||
srv = ::sqlite3_finalize(stmt);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (srv != SQLITE_OK) {
|
||||
char *msg = ::PR_smprintf("Could not finalize SQL statement '%s' (%x)",
|
||||
::sqlite3_sql(stmt),
|
||||
stmt);
|
||||
NS_WARNING(msg);
|
||||
::PR_smprintf_free(msg);
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
// Ensure that the loop continues properly, whether closing has succeeded
|
||||
// or not.
|
||||
if (srv == SQLITE_OK) {
|
||||
stmt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Now that all statements have been finalized, we
|
||||
// shoudl be able to close.
|
||||
srv = ::sqlite3_close(mDBConn);
|
||||
|
||||
}
|
||||
|
||||
if (srv != SQLITE_OK) {
|
||||
MOZ_ASSERT(srv == SQLITE_OK,
|
||||
"sqlite3_close failed. There are probably outstanding statements that are listed above!");
|
||||
}
|
||||
|
||||
mDBConn = nullptr;
|
||||
return convertResultCode(srv);
|
||||
@ -1047,7 +1076,7 @@ Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
nsIEventTarget *asyncThread = getAsyncExecutionTarget();
|
||||
NS_ENSURE_TRUE(asyncThread, NS_ERROR_UNEXPECTED);
|
||||
NS_ENSURE_TRUE(asyncThread, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
nsresult rv = setClosedState();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -1063,7 +1092,7 @@ Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
|
||||
{
|
||||
// We need to lock because we're modifying mAsyncExecutionThread
|
||||
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
|
||||
closeEvent = new AsyncCloseConnection(this, NS_GetCurrentThread(),
|
||||
closeEvent = new AsyncCloseConnection(this,
|
||||
completeEvent,
|
||||
mAsyncExecutionThread.forget());
|
||||
}
|
||||
|
@ -47,6 +47,14 @@ public:
|
||||
StatementData()
|
||||
{
|
||||
}
|
||||
~StatementData()
|
||||
{
|
||||
// We need to ensure that mParamsArray is released on the main thread,
|
||||
// as the binding arguments may be XPConnect values, which are safe
|
||||
// to release only on the main thread.
|
||||
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
||||
(void)NS_ProxyRelease(mainThread, mParamsArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sqlite statement, fetching it from the storage statement. In
|
||||
@ -77,7 +85,7 @@ public:
|
||||
* NULLs out our sqlite3_stmt (it is held by the owner) after reseting it and
|
||||
* clear all bindings to it. This is expected to occur on the async thread.
|
||||
*/
|
||||
inline void finalize()
|
||||
inline void reset()
|
||||
{
|
||||
NS_PRECONDITION(mStatementOwner, "Must have a statement owner!");
|
||||
#ifdef DEBUG
|
||||
|
@ -228,9 +228,13 @@ function test_double_asyncClose_throws()
|
||||
try {
|
||||
conn.asyncClose();
|
||||
do_throw("should have thrown");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_UNEXPECTED);
|
||||
// There is a small race condition here, which can cause either of
|
||||
// Cr.NS_ERROR_NOT_INITIALIZED or Cr.NS_ERROR_UNEXPECTED to be thrown.
|
||||
} catch (e if "result" in e && e.result == Cr.NS_ERROR_NOT_INITIALIZED) {
|
||||
do_print("NS_ERROR_NOT_INITIALIZED");
|
||||
} catch (e if "result" in e && e.result == Cr.NS_ERROR_UNEXPECTED) {
|
||||
do_print("NS_ERROR_UNEXPECTED");
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
// Reset gDBConn so that later tests will get a new connection object.
|
||||
|
@ -289,7 +289,6 @@ add_task(function test_asyncClose_succeeds_with_finalized_async_statement()
|
||||
stmt.executeAsync();
|
||||
stmt.finalize();
|
||||
|
||||
let deferred = Promise.defer();
|
||||
yield asyncClose(getOpenedDatabase());
|
||||
// Reset gDBConn so that later tests will get a new connection object.
|
||||
gDBConn = null;
|
||||
@ -359,7 +358,7 @@ function standardAsyncTest(promisedDB, name, shouldInit = false) {
|
||||
}
|
||||
|
||||
// Generate a name to insert and fetch back
|
||||
let name = "worker bee " + Math.random() + " (" + name + ")";
|
||||
name = "worker bee " + Math.random() + " (" + name + ")";
|
||||
|
||||
let stmt = adb.createAsyncStatement("INSERT INTO test (name) VALUES (:name)");
|
||||
stmt.params.name = name;
|
||||
|
@ -1333,11 +1333,13 @@ class XPCShellTests(object):
|
||||
break
|
||||
test.start()
|
||||
test.join()
|
||||
self.addTestResults(test)
|
||||
# did the test encounter any exception?
|
||||
if test.exception:
|
||||
raise test.exception
|
||||
exceptions.append(test.exception)
|
||||
tracebacks.append(test.traceback)
|
||||
break
|
||||
keep_going = test.keep_going
|
||||
self.addTestResults(test)
|
||||
|
||||
# restore default SIGINT behaviour
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
@ -676,10 +676,10 @@ this.SocialService = {
|
||||
|
||||
},
|
||||
|
||||
uninstallProvider: function(origin) {
|
||||
uninstallProvider: function(origin, aCallback) {
|
||||
let manifest = SocialServiceInternal.getManifestByOrigin(origin);
|
||||
let addon = new AddonWrapper(manifest);
|
||||
addon.uninstall();
|
||||
addon.uninstall(aCallback);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1071,12 +1071,14 @@ var SocialAddonProvider = {
|
||||
aCallback([new AddonWrapper(a) for each (a in SocialServiceInternal.manifests)]);
|
||||
},
|
||||
|
||||
removeAddon: function(aAddon) {
|
||||
removeAddon: function(aAddon, aCallback) {
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalling", aAddon, false);
|
||||
aAddon.pendingOperations |= AddonManager.PENDING_UNINSTALL;
|
||||
Services.prefs.clearUserPref(getPrefnameFromOrigin(aAddon.manifest.origin));
|
||||
aAddon.pendingOperations -= AddonManager.PENDING_UNINSTALL;
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalled", aAddon);
|
||||
if (aCallback)
|
||||
schedule(aCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1245,16 +1247,18 @@ AddonWrapper.prototype = {
|
||||
return val;
|
||||
},
|
||||
|
||||
uninstall: function() {
|
||||
uninstall: function(aCallback) {
|
||||
let prefName = getPrefnameFromOrigin(this.manifest.origin);
|
||||
if (Services.prefs.prefHasUserValue(prefName)) {
|
||||
if (ActiveProviders.has(this.manifest.origin)) {
|
||||
SocialService.removeProvider(this.manifest.origin, function() {
|
||||
SocialAddonProvider.removeAddon(this);
|
||||
SocialAddonProvider.removeAddon(this, aCallback);
|
||||
}.bind(this));
|
||||
} else {
|
||||
SocialAddonProvider.removeAddon(this);
|
||||
SocialAddonProvider.removeAddon(this, aCallback);
|
||||
}
|
||||
} else {
|
||||
schedule(aCallback);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -3502,6 +3502,12 @@
|
||||
"kind": "boolean",
|
||||
"description": "The number of failed ICE Connections (0) vs. number of successful ICE connections (1)."
|
||||
},
|
||||
"WEBRTC_CALL_DURATION":{
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
"n_buckets": "1000",
|
||||
"description": "The length of time (in seconds) that a call lasted."
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_LOCAL_TRACERDETACH_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
|
@ -1,40 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* We used to use this component to heuristically determine which links to
|
||||
* direct to the user's default browser, but we no longer use that heuristic.
|
||||
* It will come in handy when we implement support for trusted apps, however,
|
||||
* so we left it in (although it is disabled in the manifest).
|
||||
*/
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
function ContentPolicy() {}
|
||||
|
||||
ContentPolicy.prototype = {
|
||||
classID: Components.ID("{75acd178-3d5a-48a7-bd92-fba383520ae6}"),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPolicy]),
|
||||
|
||||
shouldLoad: function(contentType, contentLocation, requestOrigin, context, mimeTypeGuess, extra) {
|
||||
|
||||
return Ci.nsIContentPolicy.ACCEPT;
|
||||
|
||||
// When rejecting a load due to a content policy violation, use this code
|
||||
// to close the window opened by window.open, if any.
|
||||
//if (context.currentURI.spec == "about:blank") {
|
||||
// context.ownerDocument.defaultView.close();
|
||||
//};
|
||||
},
|
||||
|
||||
shouldProcess: function(contentType, contentLocation, requestOrigin, context, mimeType, extra) {
|
||||
return Ci.nsIContentPolicy.ACCEPT;
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentPolicy]);
|
@ -7,15 +7,6 @@ category command-line-handler x-default @mozilla.org/webapprt/clh;1
|
||||
component {07ef5b2e-88fb-47bd-8cec-d3b0bef11ac4} ContentPermission.js
|
||||
contract @mozilla.org/content-permission/prompt;1 {07ef5b2e-88fb-47bd-8cec-d3b0bef11ac4}
|
||||
|
||||
# ContentPolicy.js
|
||||
# We used to use this component to heuristically determine which links to
|
||||
# direct to the user's default browser, but we no longer use that heuristic.
|
||||
# It will come in handy when we implement support for trusted apps, however,
|
||||
# so we left it in (although it is disabled here).
|
||||
# component {75acd178-3d5a-48a7-bd92-fba383520ae6} ContentPolicy.js
|
||||
# contract @mozilla.org/webapprt/content-policy;1 {75acd178-3d5a-48a7-bd92-fba383520ae6}
|
||||
# category content-policy webapprt-content-policy @mozilla.org/webapprt/content-policy;1
|
||||
|
||||
# DirectoryProvider.js
|
||||
component {e1799fda-4b2f-4457-b671-e0641d95698d} DirectoryProvider.js
|
||||
contract @mozilla.org/webapprt/directory-provider;1 {e1799fda-4b2f-4457-b671-e0641d95698d}
|
||||
|
@ -17,7 +17,6 @@ TEST_DIRS += ['test']
|
||||
EXTRA_COMPONENTS += [
|
||||
'CommandLineHandler.js',
|
||||
'ContentPermission.js',
|
||||
'ContentPolicy.js',
|
||||
'DirectoryProvider.js',
|
||||
'components.manifest',
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user