Merge m-c to b2g-inbound.

This commit is contained in:
Ryan VanderMeulen 2013-08-27 22:39:22 -04:00
commit f7fa3b9a38
84 changed files with 1536 additions and 811 deletions

View File

@ -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":

View File

@ -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>

View File

@ -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 \

View File

@ -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>

View File

@ -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();

View File

@ -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();
}
}
}

View File

@ -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) {

View File

@ -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.
*/

View File

@ -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;

View File

@ -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 \

View File

@ -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);

View 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();
}

View File

@ -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.
*/

View File

@ -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;"

View File

@ -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)

View File

@ -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();
});
}
});
}

View File

@ -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);
},
/**

View File

@ -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);
}

View File

@ -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)

View File

@ -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],

View File

@ -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";
}

View File

@ -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";
}

View File

@ -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

View File

@ -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">

View File

@ -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;
}
},

View File

@ -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();
}

View File

@ -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);
},

View File

@ -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":

View File

@ -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();
},

View File

@ -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 \

View File

@ -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,

View File

@ -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 });
}
});

View File

@ -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/"
}

View File

@ -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();
}
});

View File

@ -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.
*

View 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();
}
};

View 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();
}
};

View File

@ -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);
}

View File

@ -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

View 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>

View 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>

View 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>

View File

@ -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

View File

@ -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) {}
}

View File

@ -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"

View File

@ -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, "*",

View File

@ -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, "*",

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -1195,6 +1195,7 @@ abstract public class GeckoApp
}
}
GeckoProfile.setAsDefault(this, getProfile().getDir());
BrowserDB.initialize(getProfile().getName());
((GeckoApplication)getApplication()).initialize();

View File

@ -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");

View File

@ -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();
}
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -283,6 +283,9 @@ size. -->
The "&#37;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 "&#37;I" string. -->
<!ENTITY home_reading_list_hint "TIP: Save articles to your reading list by long pressing the &#37;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">

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -12,6 +12,8 @@ public class testCheck2 extends PixelTest {
blockForGeckoReady();
loadAndPaint(url);
verifyHomePagerHidden();
mDriver.setupScrollHandling();
/*

View File

@ -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();
},

View File

@ -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));
},

View File

@ -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");
}
});
}
};

View 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) {

View File

@ -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", "");
});
},

View File

@ -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);

View File

@ -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);

View File

@ -59,6 +59,7 @@ body {
}
.domain-border {
margin-top: 15px;
border-bottom: 1.5px solid #777777;
width: 50%;
}

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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;

View File

@ -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());
}

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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)

View File

@ -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);
}
},

View File

@ -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",

View File

@ -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]);

View File

@ -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}

View File

@ -17,7 +17,6 @@ TEST_DIRS += ['test']
EXTRA_COMPONENTS += [
'CommandLineHandler.js',
'ContentPermission.js',
'ContentPolicy.js',
'DirectoryProvider.js',
'components.manifest',
]