Merge m-c to inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-04-16 16:21:05 -04:00
commit 239f027c15
67 changed files with 952 additions and 943 deletions

View File

@ -22,4 +22,8 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 1153796: Merge Bluetooth backend interfaces
Bug 1154235: Merge BluetoothUtils.{cpp,h} variants into single file
This patch set renames and removes source files. This requires updating
the build system's dependency information from scratch. The issue has
been reported in bug 1154232.

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="29fde3db031b910f1c9d1a35a3aab022872de205"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3cd0a9facce26c2acc7be3755a17131a6358e33f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="29fde3db031b910f1c9d1a35a3aab022872de205"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3cd0a9facce26c2acc7be3755a17131a6358e33f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="29fde3db031b910f1c9d1a35a3aab022872de205"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3cd0a9facce26c2acc7be3755a17131a6358e33f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a1ba73572c713e298ae53821d000fc3a5087b5f"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="29fde3db031b910f1c9d1a35a3aab022872de205"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3cd0a9facce26c2acc7be3755a17131a6358e33f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="6b0721ca0e92788df30daf8f7a5fb2863544f9c8">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="29fde3db031b910f1c9d1a35a3aab022872de205"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3cd0a9facce26c2acc7be3755a17131a6358e33f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="29fde3db031b910f1c9d1a35a3aab022872de205"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3cd0a9facce26c2acc7be3755a17131a6358e33f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="29fde3db031b910f1c9d1a35a3aab022872de205"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3cd0a9facce26c2acc7be3755a17131a6358e33f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="29fde3db031b910f1c9d1a35a3aab022872de205"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3cd0a9facce26c2acc7be3755a17131a6358e33f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a1ba73572c713e298ae53821d000fc3a5087b5f"/>

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "29fde3db031b910f1c9d1a35a3aab022872de205",
"git_revision": "3cd0a9facce26c2acc7be3755a17131a6358e33f",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "2511908f59de62fbba1461a6dd0b23c16ba0be63",
"revision": "c5c31cccfb22048626664d063643ae1fb1e2facc",
"repo_path": "integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="29fde3db031b910f1c9d1a35a3aab022872de205"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3cd0a9facce26c2acc7be3755a17131a6358e33f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a1ba73572c713e298ae53821d000fc3a5087b5f"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="6b0721ca0e92788df30daf8f7a5fb2863544f9c8">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="29fde3db031b910f1c9d1a35a3aab022872de205"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3cd0a9facce26c2acc7be3755a17131a6358e33f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -251,6 +251,8 @@ pref("browser.uitour.loglevel", "Error");
pref("browser.uitour.requireSecure", true);
pref("browser.uitour.themeOrigin", "https://addons.mozilla.org/%LOCALE%/firefox/themes/");
pref("browser.uitour.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tour/");
// This is used as a regexp match against the page's URL.
pref("browser.uitour.readerViewTrigger", "^https:\/\/www\.mozilla\.org\/[^\/]+\/firefox\/reading\/start");
pref("browser.customizemode.tip0.shown", false);
pref("browser.customizemode.tip0.learnMoreUrl", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/customize");

View File

@ -4203,6 +4203,7 @@ var XULBrowserWindow = {
BookmarkingUI.onLocationChange();
SocialUI.updateState(location);
UITour.onLocationChange(location);
}
// Utility functions for disabling find

View File

@ -560,7 +560,7 @@ loop.conversationViews = (function(mozL10n) {
React.createElement("button", {className: emailClasses,
onClick: this.emailLink,
disabled: this.state.emailLinkButtonDisabled},
mozL10n.get("share_button2")
mozL10n.get("share_button3")
)
)
)

View File

@ -560,7 +560,7 @@ loop.conversationViews = (function(mozL10n) {
<button className={emailClasses}
onClick={this.emailLink}
disabled={this.state.emailLinkButtonDisabled}>
{mozL10n.get("share_button2")}
{mozL10n.get("share_button3")}
</button>
</div>
</div>

View File

@ -1103,6 +1103,12 @@ body[dir=rtl] .share-service-dropdown .share-panel-header {
position: absolute;
width: 100%;
right: 0px;
/* Override the 100% specified in the .standalone-room-info selector
block so that this div doesn't take over the _whole_ screen and
transparently occlude UI widgetry (like the Join button), making
it unusable. */
height: auto;
}
.standalone-context-url {

View File

@ -200,6 +200,7 @@ loop.shared.actions = (function() {
* dispatched when a stream connects for the first time.
*/
VideoDimensionsChanged: Action.define("videoDimensionsChanged", {
isLocal: Boolean,
videoType: String,
dimensions: Object
}),

View File

@ -85,15 +85,17 @@ let CtypesHelpers = {
},
/**
* Converts a FILETIME struct (2 DWORDS), to a SYSTEMTIME struct.
* Converts a FILETIME struct (2 DWORDS), to a SYSTEMTIME struct,
* and then deduces the number of seconds since the epoch (which
* is the data we want for the cookie expiry date).
*
* @param aTimeHi
* Least significant DWORD.
* @param aTimeLo
* Most significant DWORD.
* @return a Date object representing the converted datetime.
* @return the number of seconds since the epoch
*/
fileTimeToDate: function CH_fileTimeToDate(aTimeHi, aTimeLo) {
fileTimeToSecondsSinceEpoch(aTimeHi, aTimeLo) {
let fileTime = this._structs.FILETIME();
fileTime.dwLowDateTime = aTimeLo;
fileTime.dwHighDateTime = aTimeHi;
@ -103,13 +105,15 @@ let CtypesHelpers = {
if (result == 0)
throw new Error(ctypes.winLastError);
return new Date(systemTime.wYear,
systemTime.wMonth - 1,
systemTime.wDay,
systemTime.wHour,
systemTime.wMinute,
systemTime.wSecond,
systemTime.wMilliseconds);
// System time is in UTC, so we use Date.UTC to get milliseconds from epoch,
// then divide by 1000 to get seconds, and round down:
return Math.floor(Date.UTC(systemTime.wYear,
systemTime.wMonth - 1,
systemTime.wDay,
systemTime.wHour,
systemTime.wMinute,
systemTime.wSecond,
systemTime.wMilliseconds) / 1000);
}
};
@ -458,8 +462,8 @@ Cookies.prototype = {
host = "." + host;
}
let expireTime = CtypesHelpers.fileTimeToDate(Number(expireTimeHi),
Number(expireTimeLo));
let expireTime = CtypesHelpers.fileTimeToSecondsSinceEpoch(Number(expireTimeHi),
Number(expireTimeLo));
Services.cookies.add(host,
path,
name,

View File

@ -1,11 +1,6 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.importGlobalProperties([ "URL" ]);
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@ -16,49 +11,27 @@ XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MigrationUtils",
"resource:///modules/MigrationUtils.jsm");
// Initialize profile.
let gProfD = do_get_profile();
// Create a fake XULAppInfo to satisfy the eventual needs of the migrators.
let XULAppInfo = {
// nsIXUlAppInfo
get vendor() "Mozilla",
get name() "XPCShell",
get ID() "xpcshell@tests.mozilla.org",
get version() "1",
get appBuildID() "2007010101",
get platformVersion() "1.0",
get platformBuildID() "2007010101",
Cu.import("resource://testing-common/AppInfo.jsm");
updateAppInfo();
// nsIXUlRuntime (partial)
get inSafeMode() false,
logConsoleErrors: true,
get OS() "XPCShell",
get XPCOMABI() "noarch-spidermonkey",
invalidateCachesOnRestart: function () {},
/**
* Migrates the requested resource and waits for the migration to be complete.
*/
function promiseMigration(migrator, resourceType) {
// Ensure resource migration is available.
let availableSources = migrator.getMigrateData(null, false);
Assert.ok((availableSources & resourceType) > 0);
// nsIWinAppHelper
get userCanElevate() false,
return new Promise (resolve => {
Services.obs.addObserver(function onMigrationEnded() {
Services.obs.removeObserver(onMigrationEnded, "Migration:Ended");
resolve();
}, "Migration:Ended", false);
QueryInterface: function (aIID) {
let interfaces = [Ci.nsIXULAppInfo, Ci.nsIXULRuntime];
if ("nsIWinAppHelper" in Ci)
interfaces.push(Ci.nsIWinAppHelper);
if (!interfaces.some(function (v) aIID.equals(v)))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
}
};
const CONTRACT_ID = "@mozilla.org/xre/app-info;1";
const CID = Components.ID("7685dac8-3637-4660-a544-928c5ec0e714}");
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
registrar.registerFactory(CID, "XULAppInfo", CONTRACT_ID, {
createInstance: function (aOuter, aIID) {
if (aOuter != null)
throw Cr.NS_ERROR_NO_AGGREGATION;
return XULAppInfo.QueryInterface(aIID);
},
QueryInterface: XPCOMUtils.generateQI(Ci.nsIFactory)
});
migrator.migrate(resourceType, null, null);
});
}

View File

@ -1,18 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
function run_test() {
do_test_pending();
add_task(function* () {
let migrator = MigrationUtils.getMigrator("ie");
// Sanity check for the source.
do_check_true(migrator.sourceExists);
// Ensure bookmarks migration is available.
let availableSources = migrator.getMigrateData(null, false);
do_check_true((availableSources & MigrationUtils.resourceTypes.BOOKMARKS) > 0);
Assert.ok(migrator.sourceExists);
// Wait for the imported bookmarks. Check that "From Internet Explorer"
// folders are created in the menu and on the toolbar.
@ -23,34 +12,25 @@ function run_test() {
PlacesUtils.toolbarFolderId ];
PlacesUtils.bookmarks.addObserver({
onItemAdded: function onItemAdded(aItemId, aParentId, aIndex, aItemType,
aURI, aTitle) {
onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle) {
if (aTitle == label) {
let index = expectedParents.indexOf(aParentId);
do_check_neq(index, -1);
Assert.notEqual(index, -1);
expectedParents.splice(index, 1);
if (expectedParents.length == 0)
PlacesUtils.bookmarks.removeObserver(this);
}
},
onBeginUpdateBatch: function () {},
onEndUpdateBatch: function () {},
onItemRemoved: function () {},
onItemChanged: function () {},
onItemVisited: function () {},
onItemMoved: function () {},
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onItemRemoved() {},
onItemChanged() {},
onItemVisited() {},
onItemMoved() {},
}, false);
// Wait for migration.
Services.obs.addObserver(function onMigrationEnded() {
Services.obs.removeObserver(onMigrationEnded, "Migration:Ended");
yield promiseMigration(migrator, MigrationUtils.resourceTypes.BOOKMARKS);
// Check the bookmarks have been imported to all the expected parents.
do_check_eq(expectedParents.length, 0);
do_test_finished();
}, "Migration:Ended", false);
migrator.migrate(MigrationUtils.resourceTypes.BOOKMARKS, null,
null);
}
// Check the bookmarks have been imported to all the expected parents.
Assert.equal(expectedParents.length, 0);
});

View File

@ -0,0 +1,67 @@
XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
"resource://gre/modules/ctypes.jsm");
add_task(function* () {
let migrator = MigrationUtils.getMigrator("ie");
// Sanity check for the source.
Assert.ok(migrator.sourceExists);
const BOOL = ctypes.bool;
const LPCTSTR = ctypes.char16_t.ptr;
let wininet = ctypes.open("Wininet");
do_register_cleanup(() => {
try {
wininet.close();
} catch (ex) {}
});
/*
BOOL InternetSetCookie(
_In_ LPCTSTR lpszUrl,
_In_ LPCTSTR lpszCookieName,
_In_ LPCTSTR lpszCookieData
);
*/
let setIECookie = wininet.declare("InternetSetCookieW",
ctypes.default_abi,
BOOL,
LPCTSTR,
LPCTSTR,
LPCTSTR);
let expiry = new Date();
expiry.setDate(expiry.getDate() + 7);
const COOKIE = {
host: "mycookietest.com",
name: "testcookie",
value: "testvalue",
expiry
};
// Sanity check.
Assert.equal(Services.cookies.countCookiesFromHost(COOKIE.host), 0,
"There are no cookies initially");
// Create the persistent cookie in IE.
let value = COOKIE.name + " = " + COOKIE.value +"; expires = " +
COOKIE.expiry.toUTCString();
let rv = setIECookie(new URL("http://" + COOKIE.host).href, null, value);
Assert.ok(rv, "Added a persistent IE cookie");
// Migrate cookies.
yield promiseMigration(migrator, MigrationUtils.resourceTypes.COOKIES);
Assert.equal(Services.cookies.countCookiesFromHost(COOKIE.host), 1,
"Migrated the expected number of cookies");
// Now check the cookie details.
let enumerator = Services.cookies.getCookiesFromHost(COOKIE.host);
Assert.ok(enumerator.hasMoreElements());
let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
Assert.equal(foundCookie.name, COOKIE.name);
Assert.equal(foundCookie.value, COOKIE.value);
Assert.equal(foundCookie.host, "." + COOKIE.host);
Assert.equal(foundCookie.expiry, Math.floor(COOKIE.expiry / 1000));
});

View File

@ -4,7 +4,8 @@ tail =
firefox-appdir = browser
skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_fx_fhr.js]
[test_IE_bookmarks.js]
skip-if = os != "win"
[test_fx_fhr.js]
[test_IE_cookies.js]
skip-if = os != "win"

View File

@ -137,7 +137,6 @@ this.PlacesUIUtils = {
get _copyableAnnotations() [
this.DESCRIPTION_ANNO,
this.LOAD_IN_SIDEBAR_ANNO,
PlacesUtils.POST_DATA_ANNO,
PlacesUtils.READ_ONLY_ANNO,
],
@ -172,7 +171,6 @@ this.PlacesUIUtils = {
);
}
let keyword = aData.keyword || null;
let annos = [];
if (aData.annos) {
annos = aData.annos.filter(function (aAnno) {
@ -180,9 +178,10 @@ this.PlacesUIUtils = {
}, this);
}
// There's no need to copy the keyword since it's bound to the bookmark url.
return new PlacesCreateBookmarkTransaction(PlacesUtils._uri(aData.uri),
aContainer, aIndex, aData.title,
keyword, annos, transactions);
null, annos, transactions);
},
/**

View File

@ -167,7 +167,8 @@
accesskey="&editBookmarkOverlay.description.accesskey;"
control="editBMPanel_descriptionField"/>
<textbox id="editBMPanel_descriptionField"
multiline="true"/>
multiline="true"
rows="4"/>
</row>
</rows>
</grid>

View File

@ -3,7 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
const ENGINE_FLAVOR = "text/x-moz-search-engine";
@ -191,19 +194,13 @@ var gSearchPane = {
document.getElementById("engineList").focus();
},
editKeyword: function(aEngine, aNewKeyword) {
editKeyword: Task.async(function* (aEngine, aNewKeyword) {
if (aNewKeyword) {
let bduplicate = false;
let eduplicate = false;
let dupName = "";
try {
let bmserv =
Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"]
.getService(Components.interfaces.nsINavBookmarksService);
if (bmserv.getURIForKeyword(aNewKeyword))
bduplicate = true;
} catch(ex) {}
// Check for duplicates in Places keywords.
let bduplicate = !!(yield PlacesUtils.keywords.fetch(aNewKeyword));
// Check for duplicates in changes we haven't committed yet
let engines = gEngineView._engineStore.engines;
@ -231,7 +228,7 @@ var gSearchPane = {
gEngineView._engineStore.changeEngine(aEngine, "alias", aNewKeyword);
gEngineView.invalidate();
return true;
},
}),
saveOneClickEnginesList: function () {
let hiddenList = [];
@ -519,11 +516,11 @@ EngineView.prototype = {
},
setCellText: function(index, column, value) {
if (column.id == "engineKeyword") {
if (!gSearchPane.editKeyword(this._engineStore.engines[index], value)) {
setTimeout(() => {
gSearchPane.editKeyword(this._engineStore.engines[index], value)
.then(valid => {
if (!valid)
document.getElementById("engineList").startEditing(index, column);
}, 0);
}
});
}
},
performAction: function(action) { },

View File

@ -3,7 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
const ENGINE_FLAVOR = "text/x-moz-search-engine";
@ -123,19 +126,13 @@ var gSearchPane = {
document.getElementById("engineList").focus();
},
editKeyword: function(aEngine, aNewKeyword) {
editKeyword: Task.async(function* (aEngine, aNewKeyword) {
if (aNewKeyword) {
let bduplicate = false;
let eduplicate = false;
let dupName = "";
try {
let bmserv =
Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"]
.getService(Components.interfaces.nsINavBookmarksService);
if (bmserv.getURIForKeyword(aNewKeyword))
bduplicate = true;
} catch(ex) {}
// Check for duplicates in Places keywords.
let bduplicate = !!(yield PlacesUtils.keywords.fetch(aNewKeyword));
// Check for duplicates in changes we haven't committed yet
let engines = gEngineView._engineStore.engines;
@ -163,7 +160,7 @@ var gSearchPane = {
gEngineView._engineStore.changeEngine(aEngine, "alias", aNewKeyword);
gEngineView.invalidate();
return true;
},
}),
saveOneClickEnginesList: function () {
let hiddenList = [];
@ -534,11 +531,11 @@ EngineView.prototype = {
},
setCellText: function(index, column, value) {
if (column.id == "engineKeyword") {
if (!gSearchPane.editKeyword(this._engineStore.engines[index], value)) {
setTimeout(() => {
gSearchPane.editKeyword(this._engineStore.engines[index], value)
.then(valid => {
if (!valid)
document.getElementById("engineList").startEditing(index, column);
}, 0);
}
});
}
},
performAction: function(action) { },

View File

@ -57,7 +57,9 @@ let RLSidebar = {
this.emptyListInfo = document.getElementById("emptyListInfo");
this.itemTemplate = document.getElementById("item-template");
this.list.addEventListener("click", event => this.onListClick(event));
// click events for middle-clicks are not sent to DOM nodes, only to the document.
document.addEventListener("click", event => this.onClick(event));
this.list.addEventListener("mousemove", event => this.onListMouseMove(event));
this.list.addEventListener("keydown", event => this.onListKeyDown(event), true);
@ -384,10 +386,10 @@ let RLSidebar = {
},
/**
* Handle a click event on the list box.
* Handle a click event on the sidebar.
* @param {Event} event - Triggering event.
*/
onListClick(event) {
onClick(event) {
let itemNode = this.findParentItemNode(event.target);
if (!itemNode)
return;

View File

@ -2,7 +2,12 @@
* 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/. */
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
const Ci = Components.interfaces;
const Cc = Components.classes;
@ -105,7 +110,7 @@ var gEngineManagerDialog = {
document.getElementById("engineList").focus();
},
editKeyword: function engineManager_editKeyword() {
editKeyword: Task.async(function* () {
var selectedEngine = gEngineView.selectedEngine;
if (!selectedEngine)
return;
@ -121,12 +126,8 @@ var gEngineManagerDialog = {
var dupName = "";
if (alias.value != "") {
try {
let bmserv = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
if (bmserv.getURIForKeyword(alias.value))
bduplicate = true;
} catch(ex) {}
// Check for duplicates in Places keywords.
bduplicate = !!(yield PlacesUtils.keywords.fetch(alias.value));
// Check for duplicates in changes we haven't committed yet
let engines = gEngineView._engineStore.engines;
@ -154,7 +155,7 @@ var gEngineManagerDialog = {
break;
}
}
},
}),
onSelect: function engineManager_onSelect() {
// Buttons only work if an engine is selected and it's not the last engine,

View File

@ -36,7 +36,7 @@
oncommand="gEngineManagerDialog.bump(-1);"
disabled="true"/>
<command id="cmd_editkeyword"
oncommand="gEngineManagerDialog.editKeyword();"
oncommand="gEngineManagerDialog.editKeyword().catch(Components.utils.reportError);"
disabled="true"/>
</commandset>

View File

@ -285,9 +285,13 @@ if (typeof Mozilla == 'undefined') {
_sendEvent('forceShowReaderIcon');
};
Mozilla.UITour.toggleReaderMode = function(feature) {
_sendEvent('toggleReaderMode');
};
})();
// Make this library Require-able.
if (typeof module !== 'undefined' && module.exports) {
module.exports = Mozilla.UITour;
module.exports = Mozilla.UITour;
}

View File

@ -27,12 +27,15 @@ XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
"resource:///modules/BrowserUITelemetry.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Metrics",
"resource://gre/modules/Metrics.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode",
"resource://gre/modules/ReaderMode.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ReaderParent",
"resource:///modules/ReaderParent.jsm");
// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
const PREF_LOG_LEVEL = "browser.uitour.loglevel";
const PREF_SEENPAGEIDS = "browser.uitour.seenPageIDs";
const PREF_READERVIEW_TRIGGER = "browser.uitour.readerViewTrigger";
const BACKGROUND_PAGE_ACTIONS_ALLOWED = new Set([
"endUrlbarCapture",
@ -190,6 +193,7 @@ this.UITour = {
}],
["privateWindow", {query: "#privatebrowsing-button"}],
["quit", {query: "#PanelUI-quit"}],
["readerMode-urlBar", {query: "#reader-mode-button"}],
["search", {
infoPanelOffsetX: 18,
infoPanelPosition: "after_start",
@ -344,6 +348,22 @@ this.UITour = {
JSON.stringify([...this.seenPageIDs]));
},
get _readerViewTriggerRegEx() {
delete this._readerViewTriggerRegEx;
let readerViewUITourTrigger = Services.prefs.getCharPref(PREF_READERVIEW_TRIGGER);
return this._readerViewTriggerRegEx = new RegExp(readerViewUITourTrigger, "i");
},
onLocationChange: function(aLocation) {
// The ReadingList/ReaderView tour page is expected to run in Reader View,
// which disables JavaScript on the page. To get around that, we
// automatically start a pre-defined tour on page load.
let originalUrl = ReaderMode.getOriginalUrl(aLocation);
if (this._readerViewTriggerRegEx.test(originalUrl)) {
this.startSubTour("readinglist");
}
},
onPageEvent: function(aMessage, aEvent) {
let browser = aMessage.target;
let window = browser.ownerDocument.defaultView;
@ -685,6 +705,13 @@ this.UITour = {
ReaderParent.forceShowReaderIcon(browser);
break;
}
case "toggleReaderMode": {
let targetPromise = this.getTarget(window, "readerMode-urlBar");
targetPromise.then(target => {
ReaderParent.toggleReaderMode({target: target.node});
});
}
}
if (!this.tourBrowsersByWindow.has(window)) {
@ -1720,6 +1747,20 @@ this.UITour = {
});
},
startSubTour: function (aFeature) {
if (aFeature != "string") {
log.error("startSubTour: No feature option specified");
return;
}
if (aFeature == "readinglist") {
ReaderParent.showReaderModeInfoPanel(browser);
} else {
log.error("startSubTour: Unknown feature option specified");
return;
}
},
addNavBarWidget: function (aTarget, aMessageManager, aCallbackID) {
if (aTarget.node) {
log.error("addNavBarWidget: can't add a widget already present:", aTarget);

View File

@ -9,30 +9,32 @@ support-files =
[browser_UITour.js]
skip-if = os == "linux" || e10s # Intermittent failures, bug 951965
[browser_UITour2.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly
# [browser_UITour3.js] Bug 1113038
# skip-if = os == "linux" || e10s # Linux: Bug 986760, Bug 989101; e10s: Bug 941428 - UITour.jsm not e10s friendly
# skip-if = os == "linux" || e10s # Linux: Bug 986760, Bug 989101; e10s: Bug 1073247 - UITour.jsm not e10s friendly
[browser_UITour_availableTargets.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly
[browser_UITour_detach_tab.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly
[browser_UITour_annotation_size_attributes.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly.
[browser_UITour_forceReaderMode.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly.
[browser_UITour_toggleReaderMode.js]
skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly
[browser_UITour_heartbeat.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly.
[browser_UITour_loop.js]
skip-if = os == "linux" || e10s # Bug 941428 - UITour.jsm not e10s friendly.
skip-if = os == "linux" || e10s # Bug 1073247 - UITour.jsm not e10s friendly.
[browser_UITour_modalDialog.js]
skip-if = os != "mac" || e10s # modal dialog disabling only working on OS X.Bug 941428 - UITour.jsm not e10s friendly
skip-if = os != "mac" || e10s # modal dialog disabling only working on OS X.Bug 1073247 - UITour.jsm not e10s friendly
[browser_UITour_observe.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly.
[browser_UITour_panel_close_annotation.js]
skip-if = true # Disabled due to frequent failures, bugs 1026310 and 1032137
[browser_UITour_registerPageID.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly
[browser_UITour_sync.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly
[browser_UITour_resetProfile.js]
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly

View File

@ -39,6 +39,7 @@ let tests = [
"devtools",
"privateWindow",
"quit",
"readerMode-urlBar",
"search",
"searchIcon",
"urlbar",
@ -69,6 +70,7 @@ let tests = [
"home",
"privateWindow",
"quit",
"readerMode-urlBar",
"search",
"searchIcon",
"urlbar",
@ -104,6 +106,7 @@ let tests = [
"devtools",
"privateWindow",
"quit",
"readerMode-urlBar",
"urlbar",
...(hasWebIDE ? ["webide"] : [])
]);

View File

@ -0,0 +1,20 @@
"use strict";
let gTestTab;
let gContentAPI;
let gContentWindow;
Components.utils.import("resource:///modules/UITour.jsm");
function test() {
UITourTest();
}
let tests = [
taskify(function*() {
ok(!gBrowser.selectedBrowser.currentURI.spec.startsWith("about:reader"), "Should not be in reader mode at start of test.");
gContentAPI.toggleReaderMode();
yield waitForConditionPromise(() => gBrowser.selectedBrowser.currentURI.spec.startsWith("about:reader"));
ok(gBrowser.selectedBrowser.currentURI.spec.startsWith("about:reader"), "Should be in reader mode now.");
})
];

View File

@ -55,7 +55,7 @@ let shutdown = Task.async(function*() {
yield AnimationsController.destroy();
// Don't assume that AnimationsPanel is defined here, it's in another file.
if (typeof AnimationsPanel !== "undefined") {
yield AnimationsPanel.destroy()
yield AnimationsPanel.destroy();
}
gToolbox = gInspector = null;
});
@ -97,8 +97,11 @@ let AnimationsController = {
}
this.initialized = promise.defer();
this.onPanelVisibilityChange = this.onPanelVisibilityChange.bind(this);
this.onNewNodeFront = this.onNewNodeFront.bind(this);
this.onAnimationMutations = this.onAnimationMutations.bind(this);
let target = gToolbox.target;
this.animationsFront = new AnimationsFront(target.client, target.form);
// Expose actor capabilities.
this.hasToggleAll = yield target.actorHasMethod("animations", "toggleAll");
@ -109,12 +112,13 @@ let AnimationsController = {
this.hasSetPlaybackRate = yield target.actorHasMethod("animationplayer",
"setPlaybackRate");
this.onPanelVisibilityChange = this.onPanelVisibilityChange.bind(this);
this.onNewNodeFront = this.onNewNodeFront.bind(this);
this.onAnimationMutations = this.onAnimationMutations.bind(this);
if (this.destroyed) {
console.warn("Could not fully initialize the AnimationsController");
return;
}
this.animationsFront = new AnimationsFront(target.client, target.form);
this.startListeners();
yield this.onNewNodeFront();
this.initialized.resolve();

View File

@ -14,6 +14,10 @@ let AnimationsPanel = {
PANEL_INITIALIZED: "panel-initialized",
initialize: Task.async(function*() {
if (AnimationsController.destroyed) {
console.warn("Could not initialize the animation-panel, controller was destroyed");
return;
}
if (this.initialized) {
return this.initialized.promise;
}

View File

@ -409,6 +409,10 @@ InspectorPanel.prototype = {
* reload
*/
set selectionCssSelector(cssSelector = null) {
if (this._panelDestroyer) {
return;
}
this._selectionCssSelector = {
selector: cssSelector,
url: this._target.url

View File

@ -15,6 +15,7 @@ support-files =
[browser_layoutview_editablemodel_stylerules.js]
[browser_layoutview_guides.js]
[browser_layoutview_rotate-labels-on-sides.js]
[browser_layoutview_tooltips.js]
[browser_layoutview_update-after-navigation.js]
[browser_layoutview_update-after-reload.js]
# [browser_layoutview_update-in-iframes.js]

View File

@ -0,0 +1,126 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the regions in the layout-view have tooltips, and that individual
// values too. Also test that values that are set from a css rule have tooltips
// referencing the rule.
const TEST_URI = "<style>" +
"#div1 { color: red; margin: 3em; }\n" +
"#div2 { border-bottom: 1px solid black; background: red; }\n" +
"html, body, #div3 { box-sizing: border-box; padding: 0 2em; }" +
"</style>" +
"<div id='div1'></div><div id='div2'></div><div id='div3'></div>";
// Test data for the tooltips over individual values.
// Each entry should contain:
// - selector: The selector for the node to be selected before starting to test
// - values: An array containing objects for each of the values that are defined
// by css rules. Each entry should contain:
// - name: the name of the property that is set by the css rule
// - ruleSelector: the selector of the rule
// - styleSheetLocation: the fileName:lineNumber
const VALUES_TEST_DATA = [{
selector: "#div1",
values: [{
name: "margin-top",
ruleSelector: "#div1",
styleSheetLocation: "null:1"
}, {
name: "margin-right",
ruleSelector: "#div1",
styleSheetLocation: "null:1"
}, {
name: "margin-bottom",
ruleSelector: "#div1",
styleSheetLocation: "null:1"
}, {
name: "margin-left",
ruleSelector: "#div1",
styleSheetLocation: "null:1"
}]
},{
selector: "#div2",
values: [{
name: "border-bottom-width",
ruleSelector: "#div2",
styleSheetLocation: "null:2"
}]
},{
selector: "#div3",
values: [{
name: "padding-top",
ruleSelector: "html, body, #div3",
styleSheetLocation: "null:3"
}, {
name: "padding-right",
ruleSelector: "html, body, #div3",
styleSheetLocation: "null:3"
}, {
name: "padding-bottom",
ruleSelector: "html, body, #div3",
styleSheetLocation: "null:3"
}, {
name: "padding-left",
ruleSelector: "html, body, #div3",
styleSheetLocation: "null:3"
}]
}];
add_task(function*() {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {toolbox, inspector, view} = yield openLayoutView();
info("Checking the regions tooltips");
ok(view.doc.querySelector("#margins").hasAttribute("title"),
"The margin region has a tooltip");
is(view.doc.querySelector("#margins").getAttribute("title"), "margin",
"The margin region has the correct tooltip content");
ok(view.doc.querySelector("#borders").hasAttribute("title"),
"The border region has a tooltip");
is(view.doc.querySelector("#borders").getAttribute("title"), "border",
"The border region has the correct tooltip content");
ok(view.doc.querySelector("#padding").hasAttribute("title"),
"The padding region has a tooltip");
is(view.doc.querySelector("#padding").getAttribute("title"), "padding",
"The padding region has the correct tooltip content");
ok(view.doc.querySelector("#content").hasAttribute("title"),
"The content region has a tooltip");
is(view.doc.querySelector("#content").getAttribute("title"), "content",
"The content region has the correct tooltip content");
for (let {selector, values} of VALUES_TEST_DATA) {
info("Selecting " + selector + " and checking the values tooltips");
yield selectNode(selector, inspector);
info("Iterate over all values");
for (let key in view.map) {
if (key === "position") {
continue;
}
let name = view.map[key].property;
let expectedTooltipData = values.find(o => o.name === name);
let el = view.doc.querySelector(view.map[key].selector);
ok(el.hasAttribute("title"), "The " + name + " value has a tooltip");
if (expectedTooltipData) {
info("The " + name + " value comes from a css rule");
let expectedTooltip = name + "\n" + expectedTooltipData.ruleSelector +
"\n" + expectedTooltipData.styleSheetLocation;
is(el.getAttribute("title"), expectedTooltip, "The tooltip is correct");
} else {
info("The " + name + " isn't set by a css rule");
is(el.getAttribute("title"), name, "The tooltip is correct");
}
}
}
});

View File

@ -441,6 +441,7 @@ LayoutView.prototype = {
for (let i in this.map) {
let selector = this.map[i].selector;
let span = this.doc.querySelector(selector);
this.updateSourceRuleTooltip(span, this.map[i].property, styleEntries);
if (span.textContent.length > 0 &&
span.textContent == this.map[i].value) {
continue;
@ -469,6 +470,40 @@ LayoutView.prototype = {
return this._lastRequest = lastRequest;
},
/**
* Update the text in the tooltip shown when hovering over a value to provide
* information about the source CSS rule that sets this value.
* @param {DOMNode} el The element that will receive the tooltip.
* @param {String} property The name of the CSS property for the tooltip.
* @param {Array} rules An array of applied rules retrieved by
* styleActor.getApplied.
*/
updateSourceRuleTooltip: function(el, property, rules) {
// Dummy element used to parse the cssText of applied rules.
let dummyEl = this.doc.createElement("div");
// Rules are in order of priority so iterate until we find the first that
// defines a value for the property.
let sourceRule, value;
for (let {rule} of rules) {
dummyEl.style.cssText = rule.cssText;
value = dummyEl.style.getPropertyValue(property);
if (value !== "") {
sourceRule = rule;
break;
}
}
let title = property;
if (sourceRule && sourceRule.selectors) {
title += "\n" + sourceRule.selectors.join(", ");
}
if (sourceRule && sourceRule.parentStyleSheet) {
title += "\n" + sourceRule.parentStyleSheet.href + ":" + sourceRule.line;
}
el.setAttribute("title", title);
},
/**
* Show the box-model highlighter on the currently selected element
* @param {Object} options Options passed to the highlighter actor

View File

@ -30,11 +30,11 @@
<div id="main">
<span class="legend" data-box="margin">&margin.tooltip;</span>
<span class="legend" data-box="margin" title="&margin.tooltip;">&margin.tooltip;</span>
<div id="margins" data-box="margin" title="&margin.tooltip;">
<span class="legend" data-box="border">&border.tooltip;</span>
<span class="legend" data-box="border" title="&border.tooltip;">&border.tooltip;</span>
<div id="borders" data-box="border" title="&border.tooltip;">
<span class="legend" data-box="padding">&padding.tooltip;</span>
<span class="legend" data-box="padding" title="&padding.tooltip;">&padding.tooltip;</span>
<div id="padding" data-box="padding" title="&padding.tooltip;">
<div id="content" data-box="content" title="&content.tooltip;">
</div>

View File

@ -16,6 +16,7 @@ Cu.import("resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils","resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ReadingList", "resource:///modules/readinglist/ReadingList.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UITour", "resource:///modules/UITour.jsm");
const gStringBundle = Services.strings.createBundle("chrome://global/locale/aboutReader.properties");
@ -188,6 +189,23 @@ let ReaderParent = {
}
},
/**
* Shows an info panel from the UITour for Reader Mode.
*
* @param browser The <browser> that the tour should be started for.
*/
showReaderModeInfoPanel(browser) {
let win = browser.ownerDocument.defaultView;
let targetPromise = UITour.getTarget(win, "readerMode-urlBar");
targetPromise.then(target => {
let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
UITour.showInfo(win, browser.messageManager, target,
browserBundle.GetStringFromName("readerView.promo.firstDetectedArticle.title"),
browserBundle.GetStringFromName("readerView.promo.firstDetectedArticle.body"),
"chrome://browser/skin/reader-tour.png");
});
},
/**
* Gets an article for a given URL. This method will download and parse a document.
*

View File

@ -93,6 +93,7 @@ browser.jar:
skin/classic/browser/session-restore.svg (../shared/incontent-icons/session-restore.svg)
skin/classic/browser/tab-crashed.svg (../shared/incontent-icons/tab-crashed.svg)
skin/classic/browser/welcome-back.svg (../shared/incontent-icons/welcome-back.svg)
skin/classic/browser/reader-tour.png (../shared/reader/reader-tour.png)
skin/classic/browser/readerMode.svg (../shared/reader/readerMode.svg)
skin/classic/browser/readinglist/icons.svg (../shared/readinglist/icons.svg)
skin/classic/browser/readinglist/readinglist-icon.svg (../shared/readinglist/readinglist-icon.svg)

View File

@ -145,6 +145,7 @@ browser.jar:
skin/classic/browser/session-restore.svg (../shared/incontent-icons/session-restore.svg)
skin/classic/browser/tab-crashed.svg (../shared/incontent-icons/tab-crashed.svg)
skin/classic/browser/welcome-back.svg (../shared/incontent-icons/welcome-back.svg)
skin/classic/browser/reader-tour.png (../shared/reader/reader-tour.png)
skin/classic/browser/readerMode.svg (../shared/reader/readerMode.svg)
skin/classic/browser/readinglist/icons.svg (../shared/readinglist/icons.svg)
skin/classic/browser/readinglist/readinglist-icon.svg (../shared/readinglist/readinglist-icon.svg)

View File

@ -355,6 +355,17 @@ description > html|a {
* End Dialog
*/
/**
* Font dialog menulist fixes
*/
#defaultFontType,
#serif,
#sans-serif,
#monospace {
min-width: 30ch;
}
/**
* Sync migration
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -26,6 +26,10 @@ notification[value="translation"][state="translating"] .messageImage {
}
}
notification[value="translation"] hbox[anonid="details"] {
overflow: hidden;
}
notification[value="translation"] button,
notification[value="translation"] menulist {
-moz-appearance: none;

View File

@ -128,6 +128,7 @@ browser.jar:
skin/classic/browser/session-restore.svg (../shared/incontent-icons/session-restore.svg)
skin/classic/browser/tab-crashed.svg (../shared/incontent-icons/tab-crashed.svg)
skin/classic/browser/welcome-back.svg (../shared/incontent-icons/welcome-back.svg)
skin/classic/browser/reader-tour.png (../shared/reader/reader-tour.png)
skin/classic/browser/readerMode.svg (../shared/reader/readerMode.svg)
skin/classic/browser/readinglist/icons.svg (../shared/readinglist/icons.svg)
skin/classic/browser/readinglist/readinglist-icon.svg (../shared/readinglist/readinglist-icon.svg)

View File

@ -31,10 +31,11 @@ UuidToString(const BluetoothUuid& aUuid, nsAString& aString)
memcpy(&uuid4, &aUuid.mUuid[10], sizeof(uint32_t));
memcpy(&uuid5, &aUuid.mUuid[14], sizeof(uint16_t));
sprintf(uuidStr, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
ntohl(uuid0), ntohs(uuid1),
ntohs(uuid2), ntohs(uuid3),
ntohl(uuid4), ntohs(uuid5));
snprintf(uuidStr, sizeof(uuidStr),
"%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
ntohl(uuid0), ntohs(uuid1),
ntohs(uuid2), ntohs(uuid3),
ntohl(uuid4), ntohs(uuid5));
aString.Truncate();
aString.AssignLiteral(uuidStr);
@ -98,7 +99,7 @@ GeneratePathFromGattId(const BluetoothGattId& aId,
/**
* |SetJsObject| is an internal function used by |BroadcastSystemMessage| only
*/
bool
static bool
SetJsObject(JSContext* aContext,
const BluetoothValue& aValue,
JS::Handle<JSObject*> aObj)
@ -219,6 +220,7 @@ BroadcastSystemMessage(const nsAString& aType,
return true;
}
#ifdef MOZ_B2G_BT_API_V2
void
DispatchReplySuccess(BluetoothReplyRunnable* aRunnable)
{
@ -281,6 +283,48 @@ DispatchStatusChangedEvent(const nsAString& aType,
NS_ENSURE_TRUE_VOID(bs);
bs->DistributeSignal(aType, NS_LITERAL_STRING(KEY_ADAPTER), data);
}
#else
// TODO: remove with bluetooth1
void
DispatchBluetoothReply(BluetoothReplyRunnable* aRunnable,
const BluetoothValue& aValue,
const nsAString& aErrorStr)
{
// Reply will be deleted by the runnable after running on main thread
BluetoothReply* reply;
if (!aErrorStr.IsEmpty()) {
nsString err(aErrorStr);
reply = new BluetoothReply(BluetoothReplyError(err));
} else {
MOZ_ASSERT(aValue.type() != BluetoothValue::T__None);
reply = new BluetoothReply(BluetoothReplySuccess(aValue));
}
aRunnable->SetReply(reply);
if (NS_FAILED(NS_DispatchToMainThread(aRunnable))) {
BT_WARNING("Failed to dispatch to main thread!");
}
}
// TODO: remove with bluetooth1
void
DispatchStatusChangedEvent(const nsAString& aType,
const nsAString& aAddress,
bool aStatus)
{
MOZ_ASSERT(NS_IsMainThread());
InfallibleTArray<BluetoothNamedValue> data;
BT_APPEND_NAMED_VALUE(data, "address", nsString(aAddress));
BT_APPEND_NAMED_VALUE(data, "status", aStatus);
BluetoothSignal signal(nsString(aType), NS_LITERAL_STRING(KEY_ADAPTER), data);
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
bs->DistributeSignal(signal);
}
#endif
bool
IsMainProcess()

View File

@ -93,6 +93,7 @@ BroadcastSystemMessage(const nsAString& aType,
// Dispatch bluetooth reply to main thread
//
#ifdef MOZ_B2G_BT_API_V2
/**
* Dispatch successful bluetooth reply with NO value to reply request.
*
@ -142,6 +143,19 @@ DispatchReplyError(BluetoothReplyRunnable* aRunnable,
void
DispatchReplyError(BluetoothReplyRunnable* aRunnable,
const enum BluetoothStatus aStatus);
#else
// TODO: remove with bluetooth1
void
DispatchBluetoothReply(BluetoothReplyRunnable* aRunnable,
const BluetoothValue& aValue,
const nsAString& aErrorStr);
// TODO: remove with bluetooth1
void
DispatchStatusChangedEvent(const nsAString& aType,
const nsAString& aDeviceAddress,
bool aStatus);
#endif
void
DispatchStatusChangedEvent(const nsAString& aType,

View File

@ -729,15 +729,6 @@ BluetoothService::Notify(const BluetoothSignal& aData)
{
nsString type = NS_LITERAL_STRING("bluetooth-pairing-request");
AutoSafeJSContext cx;
JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
NS_ENSURE_TRUE_VOID(obj);
if (!SetJsObject(cx, aData.value(), obj)) {
BT_WARNING("Failed to set properties of system message!");
return;
}
BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aData.name()).get());
if (aData.name().EqualsLiteral("RequestConfirmation")) {
@ -765,13 +756,7 @@ BluetoothService::Notify(const BluetoothSignal& aData)
return;
}
nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
do_GetService("@mozilla.org/system-message-internal;1");
NS_ENSURE_TRUE_VOID(systemMessenger);
JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*obj));
systemMessenger->BroadcastMessage(type, value,
JS::UndefinedHandleValue);
BroadcastSystemMessage(type, aData.value());
}
void

View File

@ -1,178 +0,0 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "BluetoothUtils.h"
#include "BluetoothReplyRunnable.h"
#include "BluetoothService.h"
#include "jsapi.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "nsContentUtils.h"
#include "nsISystemMessagesInternal.h"
#include "nsServiceManagerUtils.h"
BEGIN_BLUETOOTH_NAMESPACE
bool
SetJsObject(JSContext* aContext,
const BluetoothValue& aValue,
JS::Handle<JSObject*> aObj)
{
MOZ_ASSERT(aContext && aObj);
if (aValue.type() != BluetoothValue::TArrayOfBluetoothNamedValue) {
BT_WARNING("SetJsObject: Invalid parameter type");
return false;
}
const nsTArray<BluetoothNamedValue>& arr =
aValue.get_ArrayOfBluetoothNamedValue();
for (uint32_t i = 0; i < arr.Length(); i++) {
JS::Rooted<JS::Value> val(aContext);
const BluetoothValue& v = arr[i].value();
switch(v.type()) {
case BluetoothValue::TnsString: {
JSString* jsData = JS_NewUCStringCopyN(aContext,
v.get_nsString().BeginReading(),
v.get_nsString().Length());
NS_ENSURE_TRUE(jsData, false);
val = STRING_TO_JSVAL(jsData);
break;
}
case BluetoothValue::Tuint32_t:
val = INT_TO_JSVAL(v.get_uint32_t());
break;
case BluetoothValue::Tbool:
val = BOOLEAN_TO_JSVAL(v.get_bool());
break;
default:
BT_WARNING("SetJsObject: Parameter is not handled");
break;
}
if (!JS_SetProperty(aContext, aObj,
NS_ConvertUTF16toUTF8(arr[i].name()).get(),
val)) {
BT_WARNING("Failed to set property");
return false;
}
}
return true;
}
bool
BroadcastSystemMessage(const nsAString& aType,
const BluetoothValue& aData)
{
mozilla::AutoSafeJSContext cx;
NS_ASSERTION(!::JS_IsExceptionPending(cx),
"Shouldn't get here when an exception is pending!");
nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
do_GetService("@mozilla.org/system-message-internal;1");
NS_ENSURE_TRUE(systemMessenger, false);
JS::Rooted<JS::Value> value(cx);
if (aData.type() == BluetoothValue::TnsString) {
JSString* jsData = JS_NewUCStringCopyN(cx,
aData.get_nsString().BeginReading(),
aData.get_nsString().Length());
value = STRING_TO_JSVAL(jsData);
} else if (aData.type() == BluetoothValue::TArrayOfBluetoothNamedValue) {
JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
if (!obj) {
BT_WARNING("Failed to new JSObject for system message!");
return false;
}
if (!SetJsObject(cx, aData, obj)) {
BT_WARNING("Failed to set properties of system message!");
return false;
}
value = JS::ObjectValue(*obj);
} else {
BT_WARNING("Not support the unknown BluetoothValue type");
return false;
}
systemMessenger->BroadcastMessage(aType, value,
JS::UndefinedHandleValue);
return true;
}
bool
BroadcastSystemMessage(const nsAString& aType,
const InfallibleTArray<BluetoothNamedValue>& aData)
{
mozilla::AutoSafeJSContext cx;
NS_ASSERTION(!::JS_IsExceptionPending(cx),
"Shouldn't get here when an exception is pending!");
JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
if (!obj) {
BT_WARNING("Failed to new JSObject for system message!");
return false;
}
if (!SetJsObject(cx, aData, obj)) {
BT_WARNING("Failed to set properties of system message!");
return false;
}
nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
do_GetService("@mozilla.org/system-message-internal;1");
NS_ENSURE_TRUE(systemMessenger, false);
JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*obj));
systemMessenger->BroadcastMessage(aType, value,
JS::UndefinedHandleValue);
return true;
}
void
DispatchBluetoothReply(BluetoothReplyRunnable* aRunnable,
const BluetoothValue& aValue,
const nsAString& aErrorStr)
{
// Reply will be deleted by the runnable after running on main thread
BluetoothReply* reply;
if (!aErrorStr.IsEmpty()) {
nsString err(aErrorStr);
reply = new BluetoothReply(BluetoothReplyError(err));
} else {
MOZ_ASSERT(aValue.type() != BluetoothValue::T__None);
reply = new BluetoothReply(BluetoothReplySuccess(aValue));
}
aRunnable->SetReply(reply);
if (NS_FAILED(NS_DispatchToMainThread(aRunnable))) {
BT_WARNING("Failed to dispatch to main thread!");
}
}
void
DispatchStatusChangedEvent(const nsAString& aType,
const nsAString& aAddress,
bool aStatus)
{
MOZ_ASSERT(NS_IsMainThread());
InfallibleTArray<BluetoothNamedValue> data;
BT_APPEND_NAMED_VALUE(data, "address", nsString(aAddress));
BT_APPEND_NAMED_VALUE(data, "status", aStatus);
BluetoothSignal signal(nsString(aType), NS_LITERAL_STRING(KEY_ADAPTER), data);
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
bs->DistributeSignal(signal);
}
END_BLUETOOTH_NAMESPACE

View File

@ -1,44 +0,0 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_dom_bluetooth_bluetoothutils_h
#define mozilla_dom_bluetooth_bluetoothutils_h
#include "BluetoothCommon.h"
#include "js/TypeDecls.h"
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothNamedValue;
class BluetoothValue;
class BluetoothReplyRunnable;
bool
SetJsObject(JSContext* aContext,
const BluetoothValue& aValue,
JS::Handle<JSObject*> aObj);
bool
BroadcastSystemMessage(const nsAString& aType,
const BluetoothValue& aData);
bool
BroadcastSystemMessage(const nsAString& aType,
const InfallibleTArray<BluetoothNamedValue>& aData);
void
DispatchBluetoothReply(BluetoothReplyRunnable* aRunnable,
const BluetoothValue& aValue,
const nsAString& aErrorStr);
void
DispatchStatusChangedEvent(const nsAString& aType,
const nsAString& aDeviceAddress,
bool aStatus);
END_BLUETOOTH_NAMESPACE
#endif

View File

@ -12,7 +12,8 @@ if CONFIG['MOZ_B2G_BT']:
SOURCES += [
'BluetoothInterface.cpp',
'BluetoothInterfaceHelpers.cpp'
'BluetoothInterfaceHelpers.cpp',
'BluetoothUtils.cpp'
]
if CONFIG['MOZ_B2G_BT_API_V2']:
@ -32,7 +33,6 @@ if CONFIG['MOZ_B2G_BT']:
'bluetooth2/BluetoothProfileController.cpp',
'bluetooth2/BluetoothReplyRunnable.cpp',
'bluetooth2/BluetoothService.cpp',
'bluetooth2/BluetoothUtils.cpp',
'bluetooth2/BluetoothUuid.cpp',
'bluetooth2/ipc/BluetoothChild.cpp',
'bluetooth2/ipc/BluetoothParent.cpp',
@ -58,7 +58,6 @@ if CONFIG['MOZ_B2G_BT']:
'bluetooth1/BluetoothPropertyContainer.cpp',
'bluetooth1/BluetoothReplyRunnable.cpp',
'bluetooth1/BluetoothService.cpp',
'bluetooth1/BluetoothUtils.cpp',
'bluetooth1/BluetoothUuid.cpp',
'bluetooth1/ipc/BluetoothChild.cpp',
'bluetooth1/ipc/BluetoothParent.cpp',

View File

@ -16,7 +16,7 @@ skip-if = buildapp == 'mulet'
[test_bug703150.xul]
skip-if = buildapp == 'mulet'
[test_bug987230.xul]
skip-if = os == 'linux' # No native mousedown event
skip-if = os == 'linux' || (os == 'win' && (os_version == '6.2' || os_version == '6.3')) # No native mousedown event on Linux, bug 1135545 for win8 intermittent failures
[test_popupReflowPos.xul]
[test_popupSizeTo.xul]
[test_popupZoom.xul]

View File

@ -9,11 +9,11 @@ import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.animation.PropertyAnimator;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.animation.ViewHelper;
import org.mozilla.gecko.lwt.LightweightTheme;
import org.mozilla.gecko.lwt.LightweightThemeDrawable;
@ -31,6 +31,7 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
@ -377,9 +378,19 @@ public class TabsPanel extends LinearLayout
public void show(Panel panelToShow) {
prepareToShow(panelToShow);
int height = getVerticalPanelHeight();
dispatchLayoutChange(getWidth(), height);
mHeaderVisible = true;
final ViewTreeObserver vto = mTabsContainer.getViewTreeObserver();
if (vto.isAlive()) {
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
vto.removeGlobalOnLayoutListener(this);
int height = getVerticalPanelHeight();
dispatchLayoutChange(getWidth(), height);
mHeaderVisible = true;
}
});
}
}
public void prepareToDrag() {
@ -460,8 +471,22 @@ public class TabsPanel extends LinearLayout
inflateLayout(mContext);
initialize();
if (mVisible)
show(mCurrentPanel);
if (mVisible) {
final ViewTreeObserver vto = getViewTreeObserver();
if (vto.isAlive()) {
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
vto.removeGlobalOnLayoutListener(this);
// If we've just inflated the tabs panel, only show it once the current
// layout pass is done to avoid displayed temporary UI states during
// relayout.
show(mCurrentPanel);
}
});
}
}
}
public void autoHidePanel() {

View File

@ -26,8 +26,20 @@ let APP_INFO = {
logConsoleErrors: true,
OS: "XPCShell",
XPCOMABI: "noarch-spidermonkey",
QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULAppInfo, Ci.nsIXULRuntime]),
invalidateCachesOnRestart: function() {},
invalidateCachesOnRestart() {},
// nsIWinAppHelper
get userCanElevate() false,
QueryInterface(iid) {
let interfaces = [ Ci.nsIXULAppInfo, Ci.nsIXULRuntime ];
if ("nsIWinAppHelper" in Ci)
interfaces.push(Ci.nsIWinAppHelper);
if (!interfaces.some(v => iid.equals(v)))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
}
};

View File

@ -68,7 +68,6 @@ Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesBackups",
@ -126,13 +125,6 @@ function notifyObservers(aTopic, aInitialImport) {
: "html");
}
function promiseSoon() {
let deferred = Promise.defer();
Services.tm.mainThread.dispatch(deferred.resolve,
Ci.nsIThread.DISPATCH_NORMAL);
return deferred.promise;
}
this.BookmarkHTMLUtils = Object.freeze({
/**
* Loads the current bookmarks hierarchy from a "bookmarks.html" file.
@ -602,17 +594,10 @@ BookmarkImporter.prototype = {
// Save the keyword.
if (keyword) {
try {
PlacesUtils.bookmarks.setKeywordForBookmark(frame.previousId, keyword);
if (postData) {
PlacesUtils.annotations.setItemAnnotation(frame.previousId,
PlacesUtils.POST_DATA_ANNO,
postData,
0,
PlacesUtils.annotations.EXPIRE_NEVER);
}
} catch(e) {
}
let kwPromise = PlacesUtils.keywords.insert({ keyword,
url: frame.previousLink.spec,
postData });
this._importPromises.push(kwPromise);
}
// Set load-in-sidebar annotation for the bookmark.
@ -629,7 +614,8 @@ BookmarkImporter.prototype = {
// Import last charset.
if (lastCharset) {
PlacesUtils.setCharsetForURI(frame.previousLink, lastCharset);
let chPromise = PlacesUtils.setCharsetForURI(frame.previousLink, lastCharset);
this._importPromises.push(chPromise);
}
},
@ -934,33 +920,35 @@ BookmarkImporter.prototype = {
PlacesUtils.bookmarks.runInBatchMode(this, aDoc);
},
importFromURL: function importFromURL(aSpec) {
let deferred = Promise.defer();
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
xhr.onload = () => {
try {
this._walkTreeForImport(xhr.responseXML);
deferred.resolve();
} catch(e) {
deferred.reject(e);
throw e;
}
};
xhr.onabort = xhr.onerror = xhr.ontimeout = () => {
deferred.reject(new Error("xmlhttprequest failed"));
};
try {
xhr.open("GET", aSpec);
importFromURL: Task.async(function* (href) {
this._importPromises = [];
yield new Promise((resolve, reject) => {
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
xhr.onload = () => {
try {
this._walkTreeForImport(xhr.responseXML);
resolve();
} catch(e) {
reject(e);
}
};
xhr.onabort = xhr.onerror = xhr.ontimeout = () => {
reject(new Error("xmlhttprequest failed"));
};
xhr.open("GET", href);
xhr.responseType = "document";
xhr.overrideMimeType("text/html");
xhr.send();
} catch (e) {
deferred.reject(e);
});
// TODO (bug 1095427) once converted to the new bookmarks API, methods will
// yield, so this hack should not be needed anymore.
try {
yield Promise.all(this._importPromises);
} finally {
delete this._importPromises;
}
return deferred.promise;
},
}),
};
function BookmarkExporter(aBookmarksTree) {
@ -1104,10 +1092,6 @@ BookmarkExporter.prototype = {
},
_writeItem: function (aItem, aIndent) {
// This is a workaround for "too much recursion" error, due to the fact
// Task.jsm still uses old on-same-tick promises. It may be removed as
// soon as bug 887923 is fixed.
yield promiseSoon();
let uri = null;
try {
uri = NetUtil.newURI(aItem.uri);
@ -1121,14 +1105,11 @@ BookmarkExporter.prototype = {
this._writeDateAttributes(aItem);
yield this._writeFaviconAttribute(aItem);
let keyword = PlacesUtils.bookmarks.getKeywordForBookmark(aItem.id);
if (aItem.keyword)
this._writeAttribute("SHORTCUTURL", escapeHtmlEntities(keyword));
let postDataAnno = aItem.annos &&
aItem.annos.find(anno => anno.name == PlacesUtils.POST_DATA_ANNO);
if (postDataAnno)
this._writeAttribute("POST_DATA", escapeHtmlEntities(postDataAnno.value));
if (aItem.keyword) {
this._writeAttribute("SHORTCUTURL", escapeHtmlEntities(aItem.keyword));
if (aItem.postData)
this._writeAttribute("POST_DATA", escapeHtmlEntities(aItem.postData));
}
if (aItem.annos && aItem.annos.some(anno => anno.name == LOAD_IN_SIDEBAR_ANNO))
this._writeAttribute("WEB_PANEL", "true");

View File

@ -14,7 +14,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/PromiseUtils.jsm");
Cu.import("resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesBackups",
@ -188,47 +188,35 @@ BookmarkImporter.prototype = {
* @resolves When the new bookmarks have been created.
* @rejects JavaScript exception.
*/
importFromURL: function BI_importFromURL(aSpec) {
let deferred = Promise.defer();
let streamObserver = {
onStreamComplete: function (aLoader, aContext, aStatus, aLength,
aResult) {
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
try {
let jsonString = converter.convertFromByteArray(aResult,
aResult.length);
deferred.resolve(this.importFromJSON(jsonString));
} catch (ex) {
Cu.reportError("Failed to import from URL: " + ex);
deferred.reject(ex);
throw ex;
importFromURL(spec) {
return new Promise((resolve, reject) => {
let streamObserver = {
onStreamComplete: (aLoader, aContext, aStatus, aLength, aResult) => {
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
try {
let jsonString = converter.convertFromByteArray(aResult,
aResult.length);
resolve(this.importFromJSON(jsonString));
} catch (ex) {
Cu.reportError("Failed to import from URL: " + ex);
reject(ex);
}
}
}.bind(this)
};
try {
var uri = NetUtil.newURI(aSpec);
let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
let channel = Services.io.newChannelFromURI2(uri,
null, // aLoadingNode
principal,
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_DATAREQUEST);
let streamLoader = Cc["@mozilla.org/network/stream-loader;1"].
createInstance(Ci.nsIStreamLoader);
};
let uri = NetUtil.newURI(spec);
let channel = NetUtil.newChannel({
uri,
loadingPrincipal: Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri),
contentPolicyType: Ci.nsIContentPolicy.TYPE_DATAREQUEST
});
let streamLoader = Cc["@mozilla.org/network/stream-loader;1"]
.createInstance(Ci.nsIStreamLoader);
streamLoader.init(streamObserver);
channel.asyncOpen(streamLoader, channel);
} catch (ex) {
deferred.reject(ex);
}
return deferred.promise;
});
},
/**
@ -256,8 +244,9 @@ BookmarkImporter.prototype = {
* @param aString
* JSON string of serialized bookmark data.
*/
importFromJSON: function BI_importFromJSON(aString) {
let deferred = Promise.defer();
importFromJSON: Task.async(function* (aString) {
this._importPromises = [];
let deferred = PromiseUtils.defer();
let nodes =
PlacesUtils.unwrapNodes(aString, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER);
@ -360,8 +349,15 @@ BookmarkImporter.prototype = {
PlacesUtils.bookmarks.runInBatchMode(batch, null);
}
return deferred.promise;
},
yield deferred.promise;
// TODO (bug 1095426) once converted to the new bookmarks API, methods will
// yield, so this hack should not be needed anymore.
try {
yield Promise.all(this._importPromises);
} finally {
delete this._importPromises;
}
}),
/**
* Takes a JSON-serialized node and inserts it into the db.
@ -452,8 +448,18 @@ BookmarkImporter.prototype = {
case PlacesUtils.TYPE_X_MOZ_PLACE:
id = PlacesUtils.bookmarks.insertBookmark(
aContainer, NetUtil.newURI(aData.uri), aIndex, aData.title);
if (aData.keyword)
PlacesUtils.bookmarks.setKeywordForBookmark(id, aData.keyword);
if (aData.keyword) {
// POST data could be set in 2 ways:
// 1. new backups have a postData property
// 2. old backups have an item annotation
let postDataAnno = aData.annos &&
aData.annos.find(anno => anno.name == PlacesUtils.POST_DATA_ANNO);
let postData = aData.postData || (postDataAnno && postDataAnno.value);
let kwPromise = PlacesUtils.keywords.insert({ keyword: aData.keyword,
url: aData.uri,
postData });
this._importPromises.push(kwPromise);
}
if (aData.tags) {
// TODO (bug 967196) the tagging service should trim by itself.
let tags = aData.tags.split(",").map(tag => tag.trim());

View File

@ -1679,6 +1679,7 @@ this.PlacesUtils = {
* - tags (string): csv string of the bookmark's tags.
* - charset (string): the last known charset of the bookmark.
* - keyword (string): the bookmark's keyword (unset if none).
* - postData (string): the bookmark's keyword postData (unset if none).
* - iconuri (string): the bookmark's favicon url.
* The last four properties are not set at all if they're irrelevant (e.g.
* |charset| is not set if no charset was previously set for the bookmark
@ -1693,7 +1694,7 @@ this.PlacesUtils = {
* resolved to null.
*/
promiseBookmarksTree: Task.async(function* (aItemGuid = "", aOptions = {}) {
let createItemInfoObject = (aRow, aIncludeParentGuid) => {
let createItemInfoObject = function* (aRow, aIncludeParentGuid) {
let item = {};
let copyProps = (...props) => {
for (let prop of props) {
@ -1732,9 +1733,11 @@ this.PlacesUtils = {
// If this throws due to an invalid url, the item will be skipped.
item.uri = NetUtil.newURI(aRow.getResultByName("url")).spec;
// Keywords are cached, so this should be decently fast.
let keyword = PlacesUtils.bookmarks.getKeywordForBookmark(itemId);
if (keyword)
item.keyword = keyword;
let entry = yield PlacesUtils.keywords.fetch({ url: item.uri });
if (entry) {
item.keyword = entry.keyword;
item.postData = entry.postData;
}
break;
case Ci.nsINavBookmarksService.TYPE_FOLDER:
item.type = PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER;
@ -1756,7 +1759,7 @@ this.PlacesUtils = {
break;
}
return item;
};
}.bind(this);
const QUERY_STR =
`WITH RECURSIVE
@ -1809,40 +1812,34 @@ this.PlacesUtils = {
return exclude;
};
let rootItem = null, rootItemCreationEx = null;
let rootItem = null;
let parentsMap = new Map();
try {
let conn = yield this.promiseDBConnection();
yield conn.executeCached(QUERY_STR,
{ tags_folder: PlacesUtils.tagsFolderId,
charset_anno: PlacesUtils.CHARSET_ANNO,
item_guid: aItemGuid }, (aRow) => {
let item;
if (!rootItem) {
let conn = yield this.promiseDBConnection();
let rows = yield conn.executeCached(QUERY_STR,
{ tags_folder: PlacesUtils.tagsFolderId,
charset_anno: PlacesUtils.CHARSET_ANNO,
item_guid: aItemGuid });
for (let row of rows) {
let item;
if (!rootItem) {
try {
// This is the first row.
try {
rootItem = item = createItemInfoObject(aRow, true);
}
catch(ex) {
// If we couldn't figure out the root item, that is just as bad
// as a failed query. Bail out.
rootItemCreationEx = ex;
throw StopIteration;
}
Object.defineProperty(rootItem, "itemsCount",
{ value: 1
, writable: true
, enumerable: false
, configurable: false });
rootItem = item = yield createItemInfoObject(row, true);
Object.defineProperty(rootItem, "itemsCount", { value: 1
, writable: true
, enumerable: false
, configurable: false });
} catch(ex) {
throw new Error("Failed to fetch the data for the root item " + ex);
}
else {
} else {
try {
// Our query guarantees that we always visit parents ahead of their
// children.
item = createItemInfoObject(aRow, false);
let parentGuid = aRow.getResultByName("parentGuid");
item = yield createItemInfoObject(row, false);
let parentGuid = row.getResultByName("parentGuid");
if (hasExcludeItemsCallback && shouldExcludeItem(item, parentGuid))
return;
continue;
let parentItem = parentsMap.get(parentGuid);
if ("children" in parentItem)
@ -1851,17 +1848,15 @@ this.PlacesUtils = {
parentItem.children = [item];
rootItem.itemsCount++;
} catch(ex) {
// This is a bogus child, report and skip it.
Cu.reportError("Failed to fetch the data for an item " + ex);
continue;
}
}
if (item.type == this.TYPE_X_MOZ_PLACE_CONTAINER)
parentsMap.set(item.guid, item);
});
} catch(e) {
throw new Error("Unable to query the database " + e);
}
if (rootItemCreationEx) {
throw new Error("Failed to fetch the data for the root item" +
rootItemCreationEx);
if (item.type == this.TYPE_X_MOZ_PLACE_CONTAINER)
parentsMap.set(item.guid, item);
}
return rootItem;

View File

@ -1,270 +1,173 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
const DESCRIPTION_ANNO = "bookmarkProperties/description";
const POST_DATA_ANNO = "bookmarkProperties/POSTData";
do_check_eq(typeof PlacesUtils, "object");
// main
function run_test() {
do_test_pending();
/*
HTML+FEATURES SUMMARY:
- import legacy bookmarks
- export as json, import, test (tests integrity of html > json)
- export as html, import, test (tests integrity of json > html)
BACKUP/RESTORE SUMMARY:
- create a bookmark in each root
- tag multiple URIs with multiple tags
- export as json, import, test
*/
// import the importer
Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
// file pointer to legacy bookmarks file
var bookmarksFileOld = OS.Path.join(do_get_cwd().path, "bookmarks.preplaces.html");
// file pointer to a new places-exported json file
var jsonFile = OS.Path.join(OS.Constants.Path.profileDir, "bookmarks.exported.json");
Task.spawn(function () {
// create bookmarks.exported.json
if ((yield OS.File.exists(jsonFile)))
yield OS.File.remove(jsonFile);
// Test importing a pre-Places canonical bookmarks file.
// 1. import bookmarks.preplaces.html
// Note: we do not empty the db before this import to catch bugs like 380999
try {
BookmarkHTMLUtils.importFromFile(bookmarksFileOld, true)
.then(after_import, do_report_unexpected_exception);
} catch(ex) { do_throw("couldn't import legacy bookmarks file: " + ex); }
});
function after_import() {
populate();
// 2. run the test-suite
Task.spawn(function() {
yield validate();
yield PlacesTestUtils.promiseAsyncUpdates();
// Test exporting a Places canonical json file.
// 1. export to bookmarks.exported.json
try {
yield BookmarkJSONUtils.exportToFile(jsonFile);
} catch(ex) { do_throw("couldn't export to file: " + ex); }
LOG("exported json");
// 2. empty bookmarks db
// 3. import bookmarks.exported.json
try {
yield BookmarkJSONUtils.importFromFile(jsonFile, true);
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
LOG("imported json");
// 4. run the test-suite
yield validate();
LOG("validated import");
yield PlacesTestUtils.promiseAsyncUpdates();
do_test_finished();
});
}
}
var tagData = [
let tagData = [
{ uri: uri("http://slint.us"), tags: ["indie", "kentucky", "music"] },
{ uri: uri("http://en.wikipedia.org/wiki/Diplodocus"), tags: ["dinosaur", "dj", "rad word"] }
];
var bookmarkData = [
let bookmarkData = [
{ uri: uri("http://slint.us"), title: "indie, kentucky, music" },
{ uri: uri("http://en.wikipedia.org/wiki/Diplodocus"), title: "dinosaur, dj, rad word" }
];
/*
populate data in each folder
(menu is populated via the html import)
*/
function populate() {
// add tags
for each(let {uri: u, tags: t} in tagData)
PlacesUtils.tagging.tagURI(u, t);
// add unfiled bookmarks
for each(let {uri: u, title: t} in bookmarkData) {
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.bookmarks.unfiledBookmarksFolder,
u, PlacesUtils.bookmarks.DEFAULT_INDEX, t);
}
// add to the toolbar
for each(let {uri: u, title: t} in bookmarkData) {
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.bookmarks.toolbarFolder,
u, PlacesUtils.bookmarks.DEFAULT_INDEX, t);
}
function run_test() {
run_next_test();
}
function validate() {
yield testCanonicalBookmarks();
yield testToolbarFolder();
/*
HTML+FEATURES SUMMARY:
- import legacy bookmarks
- export as json, import, test (tests integrity of html > json)
- export as html, import, test (tests integrity of json > html)
BACKUP/RESTORE SUMMARY:
- create a bookmark in each root
- tag multiple URIs with multiple tags
- export as json, import, test
*/
add_task(function* () {
// Remove eventual bookmarks.exported.json.
let jsonFile = OS.Path.join(OS.Constants.Path.profileDir, "bookmarks.exported.json");
if ((yield OS.File.exists(jsonFile)))
yield OS.File.remove(jsonFile);
// Test importing a pre-Places canonical bookmarks file.
// Note: we do not empty the db before this import to catch bugs like 380999
let htmlFile = OS.Path.join(do_get_cwd().path, "bookmarks.preplaces.html");
yield BookmarkHTMLUtils.importFromFile(htmlFile, true);
// Populate the database.
for (let { uri, tags } of tagData) {
PlacesUtils.tagging.tagURI(uri, tags);
}
for (let { uri, title } of bookmarkData) {
yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: uri,
title });
}
for (let { uri, title } of bookmarkData) {
yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
url: uri,
title });
}
yield validate();
// Test exporting a Places canonical json file.
// 1. export to bookmarks.exported.json
yield BookmarkJSONUtils.exportToFile(jsonFile);
do_print("exported json");
// 2. empty bookmarks db
// 3. import bookmarks.exported.json
yield BookmarkJSONUtils.importFromFile(jsonFile, true);
do_print("imported json");
// 4. run the test-suite
yield validate();
do_print("validated import");
});
function* validate() {
yield testMenuBookmarks();
yield testToolbarBookmarks();
testUnfiledBookmarks();
testTags();
yield PlacesTestUtils.promiseAsyncUpdates();
}
// Tests a bookmarks datastore that has a set of bookmarks, etc
// that flex each supported field and feature.
function testCanonicalBookmarks() {
// query to see if the deleted folder and items have been imported
var query = PlacesUtils.history.getNewQuery();
query.setFolders([PlacesUtils.bookmarksMenuFolderId], 1);
var result = PlacesUtils.history.executeQuery(query, PlacesUtils.history.getNewQueryOptions());
var rootNode = result.root;
rootNode.containerOpen = true;
function* testMenuBookmarks() {
let root = PlacesUtils.getFolderContents(PlacesUtils.bookmarksMenuFolderId).root;
Assert.equal(root.childCount, 3);
// Count expected bookmarks in the menu root.
do_check_eq(rootNode.childCount, 3);
let separatorNode = root.getChild(1);
Assert.equal(separatorNode.type, separatorNode.RESULT_TYPE_SEPARATOR);
// check separator
var testSeparator = rootNode.getChild(1);
do_check_eq(testSeparator.type, testSeparator.RESULT_TYPE_SEPARATOR);
let folderNode = root.getChild(2);
Assert.equal(folderNode.type, folderNode.RESULT_TYPE_FOLDER);
Assert.equal(folderNode.title, "test");
let folder = yield PlacesUtils.bookmarks.fetch(folderNode.bookmarkGuid);
Assert.equal(folder.dateAdded.getTime(), 1177541020000);
// get test folder
var testFolder = rootNode.getChild(2);
do_check_eq(testFolder.type, testFolder.RESULT_TYPE_FOLDER);
do_check_eq(testFolder.title, "test");
Assert.equal(PlacesUtils.asQuery(folderNode).hasChildren, true);
/*
// add date
do_check_eq(PlacesUtils.bookmarks.getItemDateAdded(testFolder.itemId)/1000000, 1177541020);
// last modified
do_check_eq(PlacesUtils.bookmarks.getItemLastModified(testFolder.itemId)/1000000, 1177541050);
*/
testFolder = testFolder.QueryInterface(Ci.nsINavHistoryQueryResultNode);
do_check_eq(testFolder.hasChildren, true);
// folder description
do_check_true(PlacesUtils.annotations.itemHasAnnotation(testFolder.itemId,
DESCRIPTION_ANNO));
do_check_eq("folder test comment",
PlacesUtils.annotations.getItemAnnotation(testFolder.itemId, DESCRIPTION_ANNO));
// open test folder, and test the children
testFolder.containerOpen = true;
var cc = testFolder.childCount;
// XXX Bug 380468
// do_check_eq(cc, 2);
do_check_eq(cc, 1);
// test bookmark 1
var testBookmark1 = testFolder.getChild(0);
// url
do_check_eq("http://test/post", testBookmark1.uri);
// title
do_check_eq("test post keyword", testBookmark1.title);
// keyword
do_check_eq("test", PlacesUtils.bookmarks.getKeywordForBookmark(testBookmark1.itemId));
// sidebar
do_check_true(PlacesUtils.annotations.itemHasAnnotation(testBookmark1.itemId,
LOAD_IN_SIDEBAR_ANNO));
/*
// add date
do_check_eq(testBookmark1.dateAdded/1000000, 1177375336);
// last modified
do_check_eq(testBookmark1.lastModified/1000000, 1177375423);
*/
// post data
do_check_true(PlacesUtils.annotations.itemHasAnnotation(testBookmark1.itemId, POST_DATA_ANNO));
do_check_eq("hidden1%3Dbar&text1%3D%25s",
PlacesUtils.annotations.getItemAnnotation(testBookmark1.itemId, POST_DATA_ANNO));
// last charset
var testURI = PlacesUtils._uri(testBookmark1.uri);
do_check_eq("ISO-8859-1", (yield PlacesUtils.getCharsetForURI(testURI)));
// description
do_check_true(PlacesUtils.annotations.itemHasAnnotation(testBookmark1.itemId,
DESCRIPTION_ANNO));
do_check_eq("item description",
PlacesUtils.annotations.getItemAnnotation(testBookmark1.itemId,
Assert.equal("folder test comment",
PlacesUtils.annotations.getItemAnnotation(folderNode.itemId,
DESCRIPTION_ANNO));
// clean up
testFolder.containerOpen = false;
rootNode.containerOpen = false;
// open test folder, and test the children
folderNode.containerOpen = true;
Assert.equal(folderNode.childCount, 1);
let bookmarkNode = folderNode.getChild(0);
Assert.equal("http://test/post", bookmarkNode.uri);
Assert.equal("test post keyword", bookmarkNode.title);
Assert.ok(PlacesUtils.annotations.itemHasAnnotation(bookmarkNode.itemId,
LOAD_IN_SIDEBAR_ANNO));
Assert.equal(bookmarkNode.dateAdded, 1177375336000000);
let entry = yield PlacesUtils.keywords.fetch({ url: bookmarkNode.uri });
Assert.equal("test", entry.keyword);
Assert.equal("hidden1%3Dbar&text1%3D%25s", entry.postData);
Assert.equal("ISO-8859-1",
(yield PlacesUtils.getCharsetForURI(NetUtil.newURI(bookmarkNode.uri))));
Assert.equal("item description",
PlacesUtils.annotations.getItemAnnotation(bookmarkNode.itemId,
DESCRIPTION_ANNO));
folderNode.containerOpen = false;
root.containerOpen = false;
}
function testToolbarFolder() {
var query = PlacesUtils.history.getNewQuery();
query.setFolders([PlacesUtils.toolbarFolderId], 1);
var result = PlacesUtils.history.executeQuery(query, PlacesUtils.history.getNewQueryOptions());
var toolbar = result.root;
toolbar.containerOpen = true;
function* testToolbarBookmarks() {
let root = PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId).root;
// child count (add 2 for pre-existing items)
do_check_eq(toolbar.childCount, bookmarkData.length + 2);
Assert.equal(root.childCount, bookmarkData.length + 2);
// livemark
var livemark = toolbar.getChild(1);
// title
do_check_eq("Latest Headlines", livemark.title);
let livemarkNode = root.getChild(1);
Assert.equal("Latest Headlines", livemarkNode.title);
let foundLivemark = yield PlacesUtils.livemarks.getLivemark({ id: livemark.itemId });
do_check_eq("http://en-us.fxfeeds.mozilla.com/en-US/firefox/livebookmarks/",
foundLivemark.siteURI.spec);
do_check_eq("http://en-us.fxfeeds.mozilla.com/en-US/firefox/headlines.xml",
foundLivemark.feedURI.spec);
let livemark = yield PlacesUtils.livemarks.getLivemark({ id: livemarkNode.itemId });
Assert.equal("http://en-us.fxfeeds.mozilla.com/en-US/firefox/livebookmarks/",
livemark.siteURI.spec);
Assert.equal("http://en-us.fxfeeds.mozilla.com/en-US/firefox/headlines.xml",
livemark.feedURI.spec);
// test added bookmark data
var child = toolbar.getChild(2);
do_check_eq(child.uri, bookmarkData[0].uri.spec);
do_check_eq(child.title, bookmarkData[0].title);
child = toolbar.getChild(3);
do_check_eq(child.uri, bookmarkData[1].uri.spec);
do_check_eq(child.title, bookmarkData[1].title);
let bookmarkNode = root.getChild(2);
Assert.equal(bookmarkNode.uri, bookmarkData[0].uri.spec);
Assert.equal(bookmarkNode.title, bookmarkData[0].title);
bookmarkNode = root.getChild(3);
Assert.equal(bookmarkNode.uri, bookmarkData[1].uri.spec);
Assert.equal(bookmarkNode.title, bookmarkData[1].title);
toolbar.containerOpen = false;
root.containerOpen = false;
}
function testUnfiledBookmarks() {
var query = PlacesUtils.history.getNewQuery();
query.setFolders([PlacesUtils.unfiledBookmarksFolderId], 1);
var result = PlacesUtils.history.executeQuery(query, PlacesUtils.history.getNewQueryOptions());
var rootNode = result.root;
rootNode.containerOpen = true;
let root = PlacesUtils.getFolderContents(PlacesUtils.unfiledBookmarksFolderId).root;
// child count (add 1 for pre-existing item)
do_check_eq(rootNode.childCount, bookmarkData.length + 1);
for (var i = 1; i < rootNode.childCount; i++) {
var child = rootNode.getChild(i);
dump(bookmarkData[i - 1].uri.spec + " == " + child.uri + "?\n");
do_check_true(bookmarkData[i - 1].uri.equals(uri(child.uri)));
do_check_eq(child.title, bookmarkData[i - 1].title);
/* WTF
Assert.equal(root.childCount, bookmarkData.length + 1);
for (let i = 1; i < root.childCount; ++i) {
let child = root.getChild(i);
Assert.equal(child.uri, bookmarkData[i - 1].uri.spec);
Assert.equal(child.title, bookmarkData[i - 1].title);
if (child.tags)
do_check_eq(child.tags, bookmarkData[i].title);
*/
Assert.equal(child.tags, bookmarkData[i - 1].title);
}
rootNode.containerOpen = false;
root.containerOpen = false;
}
function testTags() {
for each(let {uri: u, tags: t} in tagData) {
var i = 0;
dump("test tags for " + u.spec + ": " + t + "\n");
var tt = PlacesUtils.tagging.getTagsForURI(u);
dump("true tags for " + u.spec + ": " + tt + "\n");
do_check_true(t.every(function(el) {
i++;
return tt.indexOf(el) > -1;
}));
do_check_eq(i, t.length);
for (let { uri, tags } of tagData) {
do_print("Test tags for " + uri.spec + ": " + tags + "\n");
let foundTags = PlacesUtils.tagging.getTagsForURI(uri);
Assert.equal(foundTags.length, tags.length);
Assert.ok(tags.every(tag => foundTags.indexOf(tag) != -1));
}
}

View File

@ -322,18 +322,21 @@ function* checkItem(aExpected, aNode)
base64EncodeString(String.fromCharCode.apply(String, data));
do_check_true(base64Icon == aExpected.icon);
break;
case "keyword":
case "keyword": {
let entry = yield PlacesUtils.keywords.fetch({ url: aNode.uri });
Assert.equal(entry.keyword, aExpected.keyword);
break;
}
case "sidebar":
do_check_eq(PlacesUtils.annotations
.itemHasAnnotation(id, LOAD_IN_SIDEBAR_ANNO),
aExpected.sidebar);
break;
case "postData":
do_check_eq(PlacesUtils.annotations
.getItemAnnotation(id, PlacesUtils.POST_DATA_ANNO),
aExpected.postData);
case "postData": {
let entry = yield PlacesUtils.keywords.fetch({ url: aNode.uri });
Assert.equal(entry.postData, aExpected.postData);
break;
}
case "charset":
let testURI = NetUtil.newURI(aNode.uri);
do_check_eq((yield PlacesUtils.getCharsetForURI(testURI)), aExpected.charset);

View File

@ -1,32 +1,10 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
/*
* This test ensures that importing/exporting to HTML does not stop
* if a malformed uri is found.
*/
// Get Services
var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
var dbConn = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
var as = Cc["@mozilla.org/browser/annotation-service;1"].
getService(Ci.nsIAnnotationService);
var icos = Cc["@mozilla.org/browser/favicon-service;1"].
getService(Ci.nsIFaviconService);
var ps = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
const DESCRIPTION_ANNO = "bookmarkProperties/description";
const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
const POST_DATA_ANNO = "bookmarkProperties/POSTData";
const TEST_FAVICON_PAGE_URL = "http://en-US.www.mozilla.com/en-US/firefox/central/";
const TEST_FAVICON_DATA_SIZE = 580;
@ -35,27 +13,27 @@ function run_test() {
run_next_test();
}
add_task(function test_corrupt_file() {
add_task(function* test_corrupt_file() {
// avoid creating the places smart folder during tests
ps.setIntPref("browser.places.smartBookmarksVersion", -1);
Services.prefs.setIntPref("browser.places.smartBookmarksVersion", -1);
// Import bookmarks from the corrupt file.
yield BookmarkHTMLUtils.importFromFile(OS.Path.join(do_get_cwd().path, "bookmarks.corrupt.html"),
true);
let corruptHtml = OS.Path.join(do_get_cwd().path, "bookmarks.corrupt.html");
yield BookmarkHTMLUtils.importFromFile(corruptHtml, true);
// Check that bookmarks that are not corrupt have been imported.
yield PlacesTestUtils.promiseAsyncUpdates();
yield database_check();
});
add_task(function test_corrupt_database() {
add_task(function* test_corrupt_database() {
// Create corruption in the database, then export.
var corruptItemId = bs.insertBookmark(bs.toolbarFolder,
uri("http://test.mozilla.org"),
bs.DEFAULT_INDEX, "We love belugas");
var stmt = dbConn.createStatement("UPDATE moz_bookmarks SET fk = NULL WHERE id = :itemId");
stmt.params.itemId = corruptItemId;
stmt.execute();
stmt.finalize();
let corruptBookmark = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
url: "http://test.mozilla.org",
title: "We love belugas" });
let db = yield PlacesUtils.promiseWrappedConnection();
yield db.execute("UPDATE moz_bookmarks SET fk = NULL WHERE guid = :guid",
{ guid: corruptBookmark.guid });
let bookmarksFile = OS.Path.join(OS.Constants.Path.profileDir, "bookmarks.exported.html");
if ((yield OS.File.exists(bookmarksFile)))
@ -65,6 +43,7 @@ add_task(function test_corrupt_database() {
// Import again and check for correctness.
remove_all_bookmarks();
yield BookmarkHTMLUtils.importFromFile(bookmarksFile, true);
yield PlacesTestUtils.promiseAsyncUpdates();
yield database_check();
});
@ -75,115 +54,81 @@ add_task(function test_corrupt_database() {
* @resolves When the checks are finished.
* @rejects Never.
*/
function database_check() {
return Task.spawn(function() {
// BOOKMARKS MENU
var query = hs.getNewQuery();
query.setFolders([bs.bookmarksMenuFolder], 1);
var options = hs.getNewQueryOptions();
options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS;
var result = hs.executeQuery(query, options);
var rootNode = result.root;
rootNode.containerOpen = true;
do_check_eq(rootNode.childCount, 2);
let database_check = Task.async(function* () {
// BOOKMARKS MENU
let root = PlacesUtils.getFolderContents(PlacesUtils.bookmarksMenuFolderId).root;
Assert.equal(root.childCount, 2);
// get test folder
var testFolder = rootNode.getChild(1);
do_check_eq(testFolder.type, testFolder.RESULT_TYPE_FOLDER);
do_check_eq(testFolder.title, "test");
// add date
do_check_eq(bs.getItemDateAdded(testFolder.itemId)/1000000, 1177541020);
// last modified
do_check_eq(bs.getItemLastModified(testFolder.itemId)/1000000, 1177541050);
testFolder = testFolder.QueryInterface(Ci.nsINavHistoryQueryResultNode);
do_check_eq(testFolder.hasChildren, true);
// folder description
do_check_true(as.itemHasAnnotation(testFolder.itemId,
DESCRIPTION_ANNO));
do_check_eq("folder test comment",
as.getItemAnnotation(testFolder.itemId, DESCRIPTION_ANNO));
// open test folder, and test the children
testFolder.containerOpen = true;
var cc = testFolder.childCount;
do_check_eq(cc, 1);
let folderNode = root.getChild(1);
Assert.equal(folderNode.type, folderNode.RESULT_TYPE_FOLDER);
Assert.equal(folderNode.title, "test");
Assert.equal(PlacesUtils.bookmarks.getItemDateAdded(folderNode.itemId), 1177541020000000);
Assert.equal(PlacesUtils.bookmarks.getItemLastModified(folderNode.itemId), 1177541050000000);
Assert.equal("folder test comment",
PlacesUtils.annotations.getItemAnnotation(folderNode.itemId,
DESCRIPTION_ANNO));
// open test folder, and test the children
PlacesUtils.asQuery(folderNode);
Assert.equal(folderNode.hasChildren, true);
folderNode.containerOpen = true;
Assert.equal(folderNode.childCount, 1);
// test bookmark 1
var testBookmark1 = testFolder.getChild(0);
// url
do_check_eq("http://test/post", testBookmark1.uri);
// title
do_check_eq("test post keyword", testBookmark1.title);
// keyword
do_check_eq("test", bs.getKeywordForBookmark(testBookmark1.itemId));
// sidebar
do_check_true(as.itemHasAnnotation(testBookmark1.itemId,
LOAD_IN_SIDEBAR_ANNO));
// add date
do_check_eq(testBookmark1.dateAdded/1000000, 1177375336);
// last modified
do_check_eq(testBookmark1.lastModified/1000000, 1177375423);
// post data
do_check_true(as.itemHasAnnotation(testBookmark1.itemId,
POST_DATA_ANNO));
do_check_eq("hidden1%3Dbar&text1%3D%25s",
as.getItemAnnotation(testBookmark1.itemId, POST_DATA_ANNO));
// last charset
var testURI = uri(testBookmark1.uri);
do_check_eq((yield PlacesUtils.getCharsetForURI(testURI)), "ISO-8859-1");
let bookmarkNode = folderNode.getChild(0);
Assert.equal("http://test/post", bookmarkNode.uri);
Assert.equal("test post keyword", bookmarkNode.title);
// description
do_check_true(as.itemHasAnnotation(testBookmark1.itemId,
DESCRIPTION_ANNO));
do_check_eq("item description",
as.getItemAnnotation(testBookmark1.itemId,
DESCRIPTION_ANNO));
let entry = yield PlacesUtils.keywords.fetch({ url: bookmarkNode.uri });
Assert.equal("test", entry.keyword);
Assert.equal("hidden1%3Dbar&text1%3D%25s", entry.postData);
// clean up
testFolder.containerOpen = false;
rootNode.containerOpen = false;
Assert.ok(PlacesUtils.annotations.itemHasAnnotation(bookmarkNode.itemId,
LOAD_IN_SIDEBAR_ANNO));
Assert.equal(bookmarkNode.dateAdded, 1177375336000000);
Assert.equal(bookmarkNode.lastModified, 1177375423000000);
// BOOKMARKS TOOLBAR
query.setFolders([bs.toolbarFolder], 1);
result = hs.executeQuery(query, hs.getNewQueryOptions());
var toolbar = result.root;
toolbar.containerOpen = true;
do_check_eq(toolbar.childCount, 3);
Assert.equal((yield PlacesUtils.getCharsetForURI(NetUtil.newURI(bookmarkNode.uri))),
"ISO-8859-1");
// livemark
var livemark = toolbar.getChild(1);
// title
do_check_eq("Latest Headlines", livemark.title);
Assert.equal("item description",
PlacesUtils.annotations.getItemAnnotation(bookmarkNode.itemId,
DESCRIPTION_ANNO));
let foundLivemark = yield PlacesUtils.livemarks.getLivemark({ id: livemark.itemId });
do_check_eq("http://en-us.fxfeeds.mozilla.com/en-US/firefox/livebookmarks/",
foundLivemark.siteURI.spec);
do_check_eq("http://en-us.fxfeeds.mozilla.com/en-US/firefox/headlines.xml",
foundLivemark.feedURI.spec);
// clean up
folderNode.containerOpen = false;
root.containerOpen = false;
// cleanup
toolbar.containerOpen = false;
// BOOKMARKS TOOLBAR
root = PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId).root;
Assert.equal(root.childCount, 3);
// UNFILED BOOKMARKS
query.setFolders([bs.unfiledBookmarksFolder], 1);
result = hs.executeQuery(query, hs.getNewQueryOptions());
var unfiledBookmarks = result.root;
unfiledBookmarks.containerOpen = true;
do_check_eq(unfiledBookmarks.childCount, 1);
unfiledBookmarks.containerOpen = false;
let livemarkNode = root.getChild(1);
Assert.equal("Latest Headlines", livemarkNode.title);
// favicons
let deferGetFaviconData = Promise.defer();
icos.getFaviconDataForPage(uri(TEST_FAVICON_PAGE_URL),
function DC_onComplete(aURI, aDataLen, aData, aMimeType) {
let livemark = yield PlacesUtils.livemarks.getLivemark({ id: livemarkNode.itemId });
Assert.equal("http://en-us.fxfeeds.mozilla.com/en-US/firefox/livebookmarks/",
livemark.siteURI.spec);
Assert.equal("http://en-us.fxfeeds.mozilla.com/en-US/firefox/headlines.xml",
livemark.feedURI.spec);
// cleanup
root.containerOpen = false;
// UNFILED BOOKMARKS
root = PlacesUtils.getFolderContents(PlacesUtils.unfiledBookmarksFolderId).root;
Assert.equal(root.childCount, 1);
root.containerOpen = false;
// favicons
yield new Promise(resolve => {
PlacesUtils.favicons.getFaviconDataForPage(uri(TEST_FAVICON_PAGE_URL),
(aURI, aDataLen, aData, aMimeType) => {
// aURI should never be null when aDataLen > 0.
do_check_neq(aURI, null);
Assert.notEqual(aURI, null);
// Favicon data is stored in the bookmarks file as a "data:" URI. For
// simplicity, instead of converting the data we receive to a "data:" URI
// and comparing it, we just check the data size.
do_check_eq(TEST_FAVICON_DATA_SIZE, aDataLen);
deferGetFaviconData.resolve();
}
);
yield deferGetFaviconData.promise;
Assert.equal(TEST_FAVICON_DATA_SIZE, aDataLen);
resolve();
});
});
}
});

View File

@ -182,16 +182,20 @@ function checkItem(aExpected, aNode) {
base64EncodeString(String.fromCharCode.apply(String, data));
do_check_true(base64Icon == aExpected.icon);
break;
case "keyword":
case "keyword": {
let entry = yield PlacesUtils.keywords.fetch({ url: aNode.uri });
Assert.equal(entry.keyword, aExpected.keyword);
break;
}
case "sidebar":
do_check_eq(PlacesUtils.annotations.itemHasAnnotation(
id, LOAD_IN_SIDEBAR_ANNO), aExpected.sidebar);
break;
case "postData":
do_check_eq(PlacesUtils.annotations.getItemAnnotation(
id, PlacesUtils.POST_DATA_ANNO), aExpected.postData);
case "postData": {
let entry = yield PlacesUtils.keywords.fetch({ url: aNode.uri });
Assert.equal(entry.postData, aExpected.postData);
break;
}
case "charset":
let testURI = NetUtil.newURI(aNode.uri);
do_check_eq((yield PlacesUtils.getCharsetForURI(testURI)), aExpected.charset);

View File

@ -729,7 +729,6 @@ add_test(function test_edit_postData() {
}
Task.spawn(function* () {
const POST_DATA_ANNO = "bookmarkProperties/POSTData";
let postData = "post-test_edit_postData";
let testURI = NetUtil.newURI("http://test_edit_postData.com");

View File

@ -3106,7 +3106,7 @@ var WalkerFront = exports.WalkerFront = protocol.FrontClass(WalkerActor, {
*/
onMutations: protocol.preEvent("new-mutations", function() {
// Fetch and process the mutations.
this.getMutations({cleanup: this.autoCleanup}).then(null, console.error);
this.getMutations({cleanup: this.autoCleanup}).catch(() => {});
}),
isLocal: function() {