author | Gregory Szorc <gps@mozilla.com> |
Thu, 14 Jul 2016 17:38:13 -0700 | |
changeset 387904 | 5563d3af0e19fb9c6b968250ee61d9171067cc43 |
parent 387903 | 820287c05a9ede8b994a2b73a8853d635c463fe7 |
child 387905 | 6643243883409cdcab405ffbbccb94cfa33603a5 |
push id | 23098 |
push user | bmo:gps@mozilla.com |
push date | Fri, 15 Jul 2016 00:40:01 +0000 |
reviewers | jaws |
bugs | 1287005 |
milestone | 50.0a1 |
--- a/browser/base/content/test/general/browser_aboutTabCrashed.js +++ b/browser/base/content/test/general/browser_aboutTabCrashed.js @@ -1,203 +1,203 @@ -"use strict"; - -const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs"; -const PAGE = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page."; -const COMMENTS = "Here's my test comment!"; -const EMAIL = "foo@privacy.com"; - -/** - * Sets up the browser to send crash reports to the local crash report - * testing server. - */ -add_task(function* setup() { - // The test harness sets MOZ_CRASHREPORTER_NO_REPORT, which disables crash - // reports. This test needs them enabled. The test also needs a mock - // report server, and fortunately one is already set up by toolkit/ - // crashreporter/test/Makefile.in. Assign its URL to MOZ_CRASHREPORTER_URL, - // which CrashSubmit.jsm uses as a server override. - let env = Cc["@mozilla.org/process/environment;1"] - .getService(Components.interfaces.nsIEnvironment); - let noReport = env.get("MOZ_CRASHREPORTER_NO_REPORT"); - let serverUrl = env.get("MOZ_CRASHREPORTER_URL"); - env.set("MOZ_CRASHREPORTER_NO_REPORT", ""); - env.set("MOZ_CRASHREPORTER_URL", SERVER_URL); - - // On debug builds, crashing tabs results in much thinking, which - // slows down the test and results in intermittent test timeouts, - // so we'll pump up the expected timeout for this test. - requestLongerTimeout(2); - - registerCleanupFunction(function() { - env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport); - env.set("MOZ_CRASHREPORTER_URL", serverUrl); - }); -}); - -/** - * This function returns a Promise that resolves once the following - * actions have taken place: - * - * 1) A new tab is opened up at PAGE - * 2) The tab is crashed - * 3) The about:tabcrashed page's fields are set in accordance with - * fieldValues - * 4) The tab is restored - * 5) A crash report is received from the testing server - * 6) Any tab crash prefs that were overwritten are reset - * - * @param fieldValues - * An Object describing how to set the about:tabcrashed - * fields. The following properties are accepted: - * - * comments (String) - * The comments to put in the comment textarea - * email (String) - * The email address to put in the email address input - * emailMe (bool) - * The checked value of the "Email me" checkbox - * includeURL (bool) - * The checked value of the "Include URL" checkbox - * - * If any of these fields are missing, the defaults from - * the user preferences are used. - * @param expectedExtra - * An Object describing the expected values that the submitted - * crash report's extra data should contain. - * @returns Promise - */ -function crashTabTestHelper(fieldValues, expectedExtra) { - return BrowserTestUtils.withNewTab({ - gBrowser, - url: PAGE, - }, function*(browser) { - let prefs = TabCrashHandler.prefs; - let originalSendReport = prefs.getBoolPref("sendReport"); - let originalEmailMe = prefs.getBoolPref("emailMe"); - let originalIncludeURL = prefs.getBoolPref("includeURL"); - let originalEmail = prefs.getCharPref("email"); - - let tab = gBrowser.getTabForBrowser(browser); - yield BrowserTestUtils.crashBrowser(browser); - let doc = browser.contentDocument; - - // Since about:tabcrashed will run in the parent process, we can safely - // manipulate its DOM nodes directly - let comments = doc.getElementById("comments"); - let email = doc.getElementById("email"); - let emailMe = doc.getElementById("emailMe"); - let includeURL = doc.getElementById("includeURL"); - - if (fieldValues.hasOwnProperty("comments")) { - comments.value = fieldValues.comments; - } - - if (fieldValues.hasOwnProperty("email")) { - email.value = fieldValues.email; - } - - if (fieldValues.hasOwnProperty("emailMe")) { - emailMe.checked = fieldValues.emailMe; - } - - if (fieldValues.hasOwnProperty("includeURL")) { - includeURL.checked = fieldValues.includeURL; - } - - let crashReport = promiseCrashReport(expectedExtra); - let restoreTab = browser.contentDocument.getElementById("restoreTab"); - restoreTab.click(); - yield BrowserTestUtils.waitForEvent(tab, "SSTabRestored"); - yield crashReport; - - // Submitting the crash report may have set some prefs regarding how to - // send tab crash reports. Let's reset them for the next test. - prefs.setBoolPref("sendReport", originalSendReport); - prefs.setBoolPref("emailMe", originalEmailMe); - prefs.setBoolPref("includeURL", originalIncludeURL); - prefs.setCharPref("email", originalEmail); - }); -} - -/** - * Tests what we send with the crash report by default. By default, we do not - * send any comments, the URL of the crashing page, or the email address of - * the user. - */ -add_task(function* test_default() { - yield crashTabTestHelper({}, { - "Comments": null, - "URL": "", - "Email": null, - }); -}); - -/** - * Test just sending a comment. - */ -add_task(function* test_just_a_comment() { - yield crashTabTestHelper({ - comments: COMMENTS, - }, { - "Comments": COMMENTS, - "URL": "", - "Email": null, - }); -}); - -/** - * Test that we don't send email if emailMe is unchecked - */ -add_task(function* test_no_email() { - yield crashTabTestHelper({ - email: EMAIL, - emailMe: false, - }, { - "Comments": null, - "URL": "", - "Email": null, - }); -}); - -/** - * Test that we can send an email address if emailMe is checked - */ -add_task(function* test_yes_email() { - yield crashTabTestHelper({ - email: EMAIL, - emailMe: true, - }, { - "Comments": null, - "URL": "", - "Email": EMAIL, - }); -}); - -/** - * Test that we will send the URL of the page if includeURL is checked. - */ -add_task(function* test_send_URL() { - yield crashTabTestHelper({ - includeURL: true, - }, { - "Comments": null, - "URL": PAGE, - "Email": null, - }); -}); - -/** - * Test that we can send comments, the email address, and the URL - */ -add_task(function* test_send_all() { - yield crashTabTestHelper({ - includeURL: true, - emailMe: true, - email: EMAIL, - comments: COMMENTS, - }, { - "Comments": COMMENTS, - "URL": PAGE, - "Email": EMAIL, - }); -}); - +"use strict"; + +const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs"; +const PAGE = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page."; +const COMMENTS = "Here's my test comment!"; +const EMAIL = "foo@privacy.com"; + +/** + * Sets up the browser to send crash reports to the local crash report + * testing server. + */ +add_task(function* setup() { + // The test harness sets MOZ_CRASHREPORTER_NO_REPORT, which disables crash + // reports. This test needs them enabled. The test also needs a mock + // report server, and fortunately one is already set up by toolkit/ + // crashreporter/test/Makefile.in. Assign its URL to MOZ_CRASHREPORTER_URL, + // which CrashSubmit.jsm uses as a server override. + let env = Cc["@mozilla.org/process/environment;1"] + .getService(Components.interfaces.nsIEnvironment); + let noReport = env.get("MOZ_CRASHREPORTER_NO_REPORT"); + let serverUrl = env.get("MOZ_CRASHREPORTER_URL"); + env.set("MOZ_CRASHREPORTER_NO_REPORT", ""); + env.set("MOZ_CRASHREPORTER_URL", SERVER_URL); + + // On debug builds, crashing tabs results in much thinking, which + // slows down the test and results in intermittent test timeouts, + // so we'll pump up the expected timeout for this test. + requestLongerTimeout(2); + + registerCleanupFunction(function() { + env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport); + env.set("MOZ_CRASHREPORTER_URL", serverUrl); + }); +}); + +/** + * This function returns a Promise that resolves once the following + * actions have taken place: + * + * 1) A new tab is opened up at PAGE + * 2) The tab is crashed + * 3) The about:tabcrashed page's fields are set in accordance with + * fieldValues + * 4) The tab is restored + * 5) A crash report is received from the testing server + * 6) Any tab crash prefs that were overwritten are reset + * + * @param fieldValues + * An Object describing how to set the about:tabcrashed + * fields. The following properties are accepted: + * + * comments (String) + * The comments to put in the comment textarea + * email (String) + * The email address to put in the email address input + * emailMe (bool) + * The checked value of the "Email me" checkbox + * includeURL (bool) + * The checked value of the "Include URL" checkbox + * + * If any of these fields are missing, the defaults from + * the user preferences are used. + * @param expectedExtra + * An Object describing the expected values that the submitted + * crash report's extra data should contain. + * @returns Promise + */ +function crashTabTestHelper(fieldValues, expectedExtra) { + return BrowserTestUtils.withNewTab({ + gBrowser, + url: PAGE, + }, function*(browser) { + let prefs = TabCrashHandler.prefs; + let originalSendReport = prefs.getBoolPref("sendReport"); + let originalEmailMe = prefs.getBoolPref("emailMe"); + let originalIncludeURL = prefs.getBoolPref("includeURL"); + let originalEmail = prefs.getCharPref("email"); + + let tab = gBrowser.getTabForBrowser(browser); + yield BrowserTestUtils.crashBrowser(browser); + let doc = browser.contentDocument; + + // Since about:tabcrashed will run in the parent process, we can safely + // manipulate its DOM nodes directly + let comments = doc.getElementById("comments"); + let email = doc.getElementById("email"); + let emailMe = doc.getElementById("emailMe"); + let includeURL = doc.getElementById("includeURL"); + + if (fieldValues.hasOwnProperty("comments")) { + comments.value = fieldValues.comments; + } + + if (fieldValues.hasOwnProperty("email")) { + email.value = fieldValues.email; + } + + if (fieldValues.hasOwnProperty("emailMe")) { + emailMe.checked = fieldValues.emailMe; + } + + if (fieldValues.hasOwnProperty("includeURL")) { + includeURL.checked = fieldValues.includeURL; + } + + let crashReport = promiseCrashReport(expectedExtra); + let restoreTab = browser.contentDocument.getElementById("restoreTab"); + restoreTab.click(); + yield BrowserTestUtils.waitForEvent(tab, "SSTabRestored"); + yield crashReport; + + // Submitting the crash report may have set some prefs regarding how to + // send tab crash reports. Let's reset them for the next test. + prefs.setBoolPref("sendReport", originalSendReport); + prefs.setBoolPref("emailMe", originalEmailMe); + prefs.setBoolPref("includeURL", originalIncludeURL); + prefs.setCharPref("email", originalEmail); + }); +} + +/** + * Tests what we send with the crash report by default. By default, we do not + * send any comments, the URL of the crashing page, or the email address of + * the user. + */ +add_task(function* test_default() { + yield crashTabTestHelper({}, { + "Comments": null, + "URL": "", + "Email": null, + }); +}); + +/** + * Test just sending a comment. + */ +add_task(function* test_just_a_comment() { + yield crashTabTestHelper({ + comments: COMMENTS, + }, { + "Comments": COMMENTS, + "URL": "", + "Email": null, + }); +}); + +/** + * Test that we don't send email if emailMe is unchecked + */ +add_task(function* test_no_email() { + yield crashTabTestHelper({ + email: EMAIL, + emailMe: false, + }, { + "Comments": null, + "URL": "", + "Email": null, + }); +}); + +/** + * Test that we can send an email address if emailMe is checked + */ +add_task(function* test_yes_email() { + yield crashTabTestHelper({ + email: EMAIL, + emailMe: true, + }, { + "Comments": null, + "URL": "", + "Email": EMAIL, + }); +}); + +/** + * Test that we will send the URL of the page if includeURL is checked. + */ +add_task(function* test_send_URL() { + yield crashTabTestHelper({ + includeURL: true, + }, { + "Comments": null, + "URL": PAGE, + "Email": null, + }); +}); + +/** + * Test that we can send comments, the email address, and the URL + */ +add_task(function* test_send_all() { + yield crashTabTestHelper({ + includeURL: true, + emailMe: true, + email: EMAIL, + comments: COMMENTS, + }, { + "Comments": COMMENTS, + "URL": PAGE, + "Email": EMAIL, + }); +}); +
--- a/browser/base/content/test/general/browser_backButtonFitts.js +++ b/browser/base/content/test/general/browser_backButtonFitts.js @@ -1,42 +1,42 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -add_task(function* () { - let firstLocation = "http://example.org/browser/browser/base/content/test/general/dummy_page.html"; - yield BrowserTestUtils.openNewForegroundTab(gBrowser, firstLocation); - - yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { - // Push the state before maximizing the window and clicking below. - content.history.pushState("page2", "page2", "page2"); - - // While in the child process, add a listener for the popstate event here. This - // event will fire when the mouse click happens. - content.addEventListener("popstate", function onPopState() { - content.removeEventListener("popstate", onPopState, false); - sendAsyncMessage("Test:PopStateOccurred", { location: content.document.location.href }); - }, false); - }); - - window.maximize(); - - // Find where the nav-bar is vertically. - var navBar = document.getElementById("nav-bar"); - var boundingRect = navBar.getBoundingClientRect(); - var yPixel = boundingRect.top + Math.floor(boundingRect.height / 2); - var xPixel = 0; // Use the first pixel of the screen since it is maximized. - - let resultLocation = yield new Promise(resolve => { - messageManager.addMessageListener("Test:PopStateOccurred", function statePopped(message) { - messageManager.removeMessageListener("Test:PopStateOccurred", statePopped); - resolve(message.data.location); - }); - - EventUtils.synthesizeMouseAtPoint(xPixel, yPixel, {}, window); - }); - - is(resultLocation, firstLocation, "Clicking the first pixel should have navigated back."); - window.restore(); - - gBrowser.removeCurrentTab(); -}); +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +add_task(function* () { + let firstLocation = "http://example.org/browser/browser/base/content/test/general/dummy_page.html"; + yield BrowserTestUtils.openNewForegroundTab(gBrowser, firstLocation); + + yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + // Push the state before maximizing the window and clicking below. + content.history.pushState("page2", "page2", "page2"); + + // While in the child process, add a listener for the popstate event here. This + // event will fire when the mouse click happens. + content.addEventListener("popstate", function onPopState() { + content.removeEventListener("popstate", onPopState, false); + sendAsyncMessage("Test:PopStateOccurred", { location: content.document.location.href }); + }, false); + }); + + window.maximize(); + + // Find where the nav-bar is vertically. + var navBar = document.getElementById("nav-bar"); + var boundingRect = navBar.getBoundingClientRect(); + var yPixel = boundingRect.top + Math.floor(boundingRect.height / 2); + var xPixel = 0; // Use the first pixel of the screen since it is maximized. + + let resultLocation = yield new Promise(resolve => { + messageManager.addMessageListener("Test:PopStateOccurred", function statePopped(message) { + messageManager.removeMessageListener("Test:PopStateOccurred", statePopped); + resolve(message.data.location); + }); + + EventUtils.synthesizeMouseAtPoint(xPixel, yPixel, {}, window); + }); + + is(resultLocation, firstLocation, "Clicking the first pixel should have navigated back."); + window.restore(); + + gBrowser.removeCurrentTab(); +});
--- a/browser/base/content/test/general/browser_bookmark_popup.js +++ b/browser/base/content/test/general/browser_bookmark_popup.js @@ -1,257 +1,257 @@ -/* 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/. */ - -"use strict"; - -/** - * Test opening and closing the bookmarks panel. - */ - -let bookmarkPanel = document.getElementById("editBookmarkPanel"); -let bookmarkStar = document.getElementById("bookmarks-menu-button"); -let bookmarkPanelTitle = document.getElementById("editBookmarkPanelTitle"); - -StarUI._closePanelQuickForTesting = true; - -function* test_bookmarks_popup({isNewBookmark, popupShowFn, popupEditFn, - shouldAutoClose, popupHideFn, isBookmarkRemoved}) { - let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home"); - try { - if (!isNewBookmark) { - yield PlacesUtils.bookmarks.insert({ - parentGuid: PlacesUtils.bookmarks.unfiledGuid, - url: "about:home", - title: "Home Page" - }); - } - - is(bookmarkStar.hasAttribute("starred"), !isNewBookmark, - "Page should only be starred prior to popupshown if editing bookmark"); - is(bookmarkPanel.state, "closed", "Panel should be 'closed' to start test"); - let shownPromise = promisePopupShown(bookmarkPanel); - yield popupShowFn(tab.linkedBrowser); - yield shownPromise; - is(bookmarkPanel.state, "open", "Panel should be 'open' after shownPromise is resolved"); - - if (popupEditFn) { - yield popupEditFn(); - } - let bookmarks = []; - yield PlacesUtils.bookmarks.fetch({url: "about:home"}, bm => bookmarks.push(bm)); - is(bookmarks.length, 1, "Only one bookmark should exist"); - is(bookmarkStar.getAttribute("starred"), "true", "Page is starred"); - is(bookmarkPanelTitle.value, - isNewBookmark ? - gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") : - gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle"), - "title should match isEditingBookmark state"); - - if (!shouldAutoClose) { - yield new Promise(resolve => setTimeout(resolve, 400)); - is(bookmarkPanel.state, "open", "Panel should still be 'open' for non-autoclose"); - } - - let hiddenPromise = promisePopupHidden(bookmarkPanel); - if (popupHideFn) { - yield popupHideFn(); - } - yield hiddenPromise; - is(bookmarkStar.hasAttribute("starred"), !isBookmarkRemoved, - "Page is starred after closing"); - } finally { - let bookmark = yield PlacesUtils.bookmarks.fetch({url: "about:home"}); - is(!!bookmark, !isBookmarkRemoved, - "bookmark should not be present if a panel action should've removed it"); - if (bookmark) { - yield PlacesUtils.bookmarks.remove(bookmark); - } - gBrowser.removeTab(tab); - } -} - -add_task(function* panel_shown_for_new_bookmarks_and_autocloses() { - yield test_bookmarks_popup({ - isNewBookmark: true, - popupShowFn() { - bookmarkStar.click(); - }, - shouldAutoClose: true, - isBookmarkRemoved: false, - }); -}); - -add_task(function* panel_shown_for_once_for_doubleclick_on_new_bookmark_star_and_autocloses() { - yield test_bookmarks_popup({ - isNewBookmark: true, - popupShowFn() { - EventUtils.synthesizeMouse(bookmarkStar, 10, 10, { clickCount: 2 }, - window); - }, - shouldAutoClose: true, - isBookmarkRemoved: false, - }); -}); - -add_task(function* panel_shown_once_for_slow_doubleclick_on_new_bookmark_star_and_autocloses() { - todo(false, "bug 1250267, may need to add some tracking state to " + - "browser-places.js for this."); - return; - - yield test_bookmarks_popup({ - isNewBookmark: true, - *popupShowFn() { - EventUtils.synthesizeMouse(bookmarkStar, 10, 10, window); - yield new Promise(resolve => setTimeout(resolve, 300)); - EventUtils.synthesizeMouse(bookmarkStar, 10, 10, window); - }, - shouldAutoClose: true, - isBookmarkRemoved: false, - }); -}); - -add_task(function* panel_shown_for_keyboardshortcut_on_new_bookmark_star_and_autocloses() { - yield test_bookmarks_popup({ - isNewBookmark: true, - popupShowFn() { - EventUtils.synthesizeKey("D", {accelKey: true}, window); - }, - shouldAutoClose: true, - isBookmarkRemoved: false, - }); -}); - -add_task(function* panel_shown_for_new_bookmarks_mousemove_mouseout() { - yield test_bookmarks_popup({ - isNewBookmark: true, - popupShowFn() { - bookmarkStar.click(); - }, - *popupEditFn() { - let mouseMovePromise = BrowserTestUtils.waitForEvent(bookmarkPanel, "mousemove"); - EventUtils.synthesizeMouseAtCenter(bookmarkPanel, {type: "mousemove"}); - info("Waiting for mousemove event"); - yield mouseMovePromise; - info("Got mousemove event"); - - yield new Promise(resolve => setTimeout(resolve, 400)); - is(bookmarkPanel.state, "open", "Panel should still be open on mousemove"); - }, - *popupHideFn() { - let mouseOutPromise = BrowserTestUtils.waitForEvent(bookmarkPanel, "mouseout"); - EventUtils.synthesizeMouse(bookmarkPanel, 0, 0, {type: "mouseout"}); - EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"}); - info("Waiting for mouseout event"); - yield mouseOutPromise; - info("Got mouseout event, should autoclose now"); - }, - shouldAutoClose: false, - isBookmarkRemoved: false, - }); -}); - -add_task(function* panel_shown_for_new_bookmark_no_autoclose_close_with_ESC() { - yield test_bookmarks_popup({ - isNewBookmark: false, - popupShowFn() { - bookmarkStar.click(); - }, - shouldAutoClose: false, - popupHideFn() { - EventUtils.synthesizeKey("VK_ESCAPE", {accelKey: true}, window); - }, - isBookmarkRemoved: false, - }); -}); - -add_task(function* panel_shown_for_editing_no_autoclose_close_with_ESC() { - yield test_bookmarks_popup({ - isNewBookmark: false, - popupShowFn() { - bookmarkStar.click(); - }, - shouldAutoClose: false, - popupHideFn() { - EventUtils.synthesizeKey("VK_ESCAPE", {accelKey: true}, window); - }, - isBookmarkRemoved: false, - }); -}); - -add_task(function* panel_shown_for_new_bookmark_keypress_no_autoclose() { - yield test_bookmarks_popup({ - isNewBookmark: true, - popupShowFn() { - bookmarkStar.click(); - }, - popupEditFn() { - EventUtils.sendChar("VK_TAB", window); - }, - shouldAutoClose: false, - popupHideFn() { - bookmarkPanel.hidePopup(); - }, - isBookmarkRemoved: false, - }); -}); - -add_task(function* contextmenu_new_bookmark_keypress_no_autoclose() { - yield test_bookmarks_popup({ - isNewBookmark: true, - *popupShowFn(browser) { - let contextMenu = document.getElementById("contentAreaContextMenu"); - let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu, - "popupshown"); - let awaitPopupHidden = BrowserTestUtils.waitForEvent(contextMenu, - "popuphidden"); - yield BrowserTestUtils.synthesizeMouseAtCenter("body", { - type: "contextmenu", - button: 2 - }, browser); - yield awaitPopupShown; - document.getElementById("context-bookmarkpage").click(); - contextMenu.hidePopup(); - yield awaitPopupHidden; - }, - popupEditFn() { - EventUtils.sendChar("VK_TAB", window); - }, - shouldAutoClose: false, - popupHideFn() { - bookmarkPanel.hidePopup(); - }, - isBookmarkRemoved: false, - }); -}); - -add_task(function* bookmarks_menu_new_bookmark_remove_bookmark() { - yield test_bookmarks_popup({ - isNewBookmark: true, - popupShowFn(browser) { - document.getElementById("menu_bookmarkThisPage").doCommand(); - }, - shouldAutoClose: true, - popupHideFn() { - document.getElementById("editBookmarkPanelRemoveButton").click(); - }, - isBookmarkRemoved: true, - }); -}); - -add_task(function* ctrl_d_edit_bookmark_remove_bookmark() { - yield test_bookmarks_popup({ - isNewBookmark: false, - popupShowFn(browser) { - EventUtils.synthesizeKey("D", {accelKey: true}, window); - }, - shouldAutoClose: true, - popupHideFn() { - document.getElementById("editBookmarkPanelRemoveButton").click(); - }, - isBookmarkRemoved: true, - }); -}); - -registerCleanupFunction(function() { - delete StarUI._closePanelQuickForTesting; -}) +/* 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/. */ + +"use strict"; + +/** + * Test opening and closing the bookmarks panel. + */ + +let bookmarkPanel = document.getElementById("editBookmarkPanel"); +let bookmarkStar = document.getElementById("bookmarks-menu-button"); +let bookmarkPanelTitle = document.getElementById("editBookmarkPanelTitle"); + +StarUI._closePanelQuickForTesting = true; + +function* test_bookmarks_popup({isNewBookmark, popupShowFn, popupEditFn, + shouldAutoClose, popupHideFn, isBookmarkRemoved}) { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home"); + try { + if (!isNewBookmark) { + yield PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "about:home", + title: "Home Page" + }); + } + + is(bookmarkStar.hasAttribute("starred"), !isNewBookmark, + "Page should only be starred prior to popupshown if editing bookmark"); + is(bookmarkPanel.state, "closed", "Panel should be 'closed' to start test"); + let shownPromise = promisePopupShown(bookmarkPanel); + yield popupShowFn(tab.linkedBrowser); + yield shownPromise; + is(bookmarkPanel.state, "open", "Panel should be 'open' after shownPromise is resolved"); + + if (popupEditFn) { + yield popupEditFn(); + } + let bookmarks = []; + yield PlacesUtils.bookmarks.fetch({url: "about:home"}, bm => bookmarks.push(bm)); + is(bookmarks.length, 1, "Only one bookmark should exist"); + is(bookmarkStar.getAttribute("starred"), "true", "Page is starred"); + is(bookmarkPanelTitle.value, + isNewBookmark ? + gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") : + gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle"), + "title should match isEditingBookmark state"); + + if (!shouldAutoClose) { + yield new Promise(resolve => setTimeout(resolve, 400)); + is(bookmarkPanel.state, "open", "Panel should still be 'open' for non-autoclose"); + } + + let hiddenPromise = promisePopupHidden(bookmarkPanel); + if (popupHideFn) { + yield popupHideFn(); + } + yield hiddenPromise; + is(bookmarkStar.hasAttribute("starred"), !isBookmarkRemoved, + "Page is starred after closing"); + } finally { + let bookmark = yield PlacesUtils.bookmarks.fetch({url: "about:home"}); + is(!!bookmark, !isBookmarkRemoved, + "bookmark should not be present if a panel action should've removed it"); + if (bookmark) { + yield PlacesUtils.bookmarks.remove(bookmark); + } + gBrowser.removeTab(tab); + } +} + +add_task(function* panel_shown_for_new_bookmarks_and_autocloses() { + yield test_bookmarks_popup({ + isNewBookmark: true, + popupShowFn() { + bookmarkStar.click(); + }, + shouldAutoClose: true, + isBookmarkRemoved: false, + }); +}); + +add_task(function* panel_shown_for_once_for_doubleclick_on_new_bookmark_star_and_autocloses() { + yield test_bookmarks_popup({ + isNewBookmark: true, + popupShowFn() { + EventUtils.synthesizeMouse(bookmarkStar, 10, 10, { clickCount: 2 }, + window); + }, + shouldAutoClose: true, + isBookmarkRemoved: false, + }); +}); + +add_task(function* panel_shown_once_for_slow_doubleclick_on_new_bookmark_star_and_autocloses() { + todo(false, "bug 1250267, may need to add some tracking state to " + + "browser-places.js for this."); + return; + + yield test_bookmarks_popup({ + isNewBookmark: true, + *popupShowFn() { + EventUtils.synthesizeMouse(bookmarkStar, 10, 10, window); + yield new Promise(resolve => setTimeout(resolve, 300)); + EventUtils.synthesizeMouse(bookmarkStar, 10, 10, window); + }, + shouldAutoClose: true, + isBookmarkRemoved: false, + }); +}); + +add_task(function* panel_shown_for_keyboardshortcut_on_new_bookmark_star_and_autocloses() { + yield test_bookmarks_popup({ + isNewBookmark: true, + popupShowFn() { + EventUtils.synthesizeKey("D", {accelKey: true}, window); + }, + shouldAutoClose: true, + isBookmarkRemoved: false, + }); +}); + +add_task(function* panel_shown_for_new_bookmarks_mousemove_mouseout() { + yield test_bookmarks_popup({ + isNewBookmark: true, + popupShowFn() { + bookmarkStar.click(); + }, + *popupEditFn() { + let mouseMovePromise = BrowserTestUtils.waitForEvent(bookmarkPanel, "mousemove"); + EventUtils.synthesizeMouseAtCenter(bookmarkPanel, {type: "mousemove"}); + info("Waiting for mousemove event"); + yield mouseMovePromise; + info("Got mousemove event"); + + yield new Promise(resolve => setTimeout(resolve, 400)); + is(bookmarkPanel.state, "open", "Panel should still be open on mousemove"); + }, + *popupHideFn() { + let mouseOutPromise = BrowserTestUtils.waitForEvent(bookmarkPanel, "mouseout"); + EventUtils.synthesizeMouse(bookmarkPanel, 0, 0, {type: "mouseout"}); + EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"}); + info("Waiting for mouseout event"); + yield mouseOutPromise; + info("Got mouseout event, should autoclose now"); + }, + shouldAutoClose: false, + isBookmarkRemoved: false, + }); +}); + +add_task(function* panel_shown_for_new_bookmark_no_autoclose_close_with_ESC() { + yield test_bookmarks_popup({ + isNewBookmark: false, + popupShowFn() { + bookmarkStar.click(); + }, + shouldAutoClose: false, + popupHideFn() { + EventUtils.synthesizeKey("VK_ESCAPE", {accelKey: true}, window); + }, + isBookmarkRemoved: false, + }); +}); + +add_task(function* panel_shown_for_editing_no_autoclose_close_with_ESC() { + yield test_bookmarks_popup({ + isNewBookmark: false, + popupShowFn() { + bookmarkStar.click(); + }, + shouldAutoClose: false, + popupHideFn() { + EventUtils.synthesizeKey("VK_ESCAPE", {accelKey: true}, window); + }, + isBookmarkRemoved: false, + }); +}); + +add_task(function* panel_shown_for_new_bookmark_keypress_no_autoclose() { + yield test_bookmarks_popup({ + isNewBookmark: true, + popupShowFn() { + bookmarkStar.click(); + }, + popupEditFn() { + EventUtils.sendChar("VK_TAB", window); + }, + shouldAutoClose: false, + popupHideFn() { + bookmarkPanel.hidePopup(); + }, + isBookmarkRemoved: false, + }); +}); + +add_task(function* contextmenu_new_bookmark_keypress_no_autoclose() { + yield test_bookmarks_popup({ + isNewBookmark: true, + *popupShowFn(browser) { + let contextMenu = document.getElementById("contentAreaContextMenu"); + let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu, + "popupshown"); + let awaitPopupHidden = BrowserTestUtils.waitForEvent(contextMenu, + "popuphidden"); + yield BrowserTestUtils.synthesizeMouseAtCenter("body", { + type: "contextmenu", + button: 2 + }, browser); + yield awaitPopupShown; + document.getElementById("context-bookmarkpage").click(); + contextMenu.hidePopup(); + yield awaitPopupHidden; + }, + popupEditFn() { + EventUtils.sendChar("VK_TAB", window); + }, + shouldAutoClose: false, + popupHideFn() { + bookmarkPanel.hidePopup(); + }, + isBookmarkRemoved: false, + }); +}); + +add_task(function* bookmarks_menu_new_bookmark_remove_bookmark() { + yield test_bookmarks_popup({ + isNewBookmark: true, + popupShowFn(browser) { + document.getElementById("menu_bookmarkThisPage").doCommand(); + }, + shouldAutoClose: true, + popupHideFn() { + document.getElementById("editBookmarkPanelRemoveButton").click(); + }, + isBookmarkRemoved: true, + }); +}); + +add_task(function* ctrl_d_edit_bookmark_remove_bookmark() { + yield test_bookmarks_popup({ + isNewBookmark: false, + popupShowFn(browser) { + EventUtils.synthesizeKey("D", {accelKey: true}, window); + }, + shouldAutoClose: true, + popupHideFn() { + document.getElementById("editBookmarkPanelRemoveButton").click(); + }, + isBookmarkRemoved: true, + }); +}); + +registerCleanupFunction(function() { + delete StarUI._closePanelQuickForTesting; +})
--- a/browser/base/content/test/general/browser_bug882977.js +++ b/browser/base/content/test/general/browser_bug882977.js @@ -1,36 +1,36 @@ -"use strict"; - -/** - * Tests that the identity-box shows the chromeUI styling - * when viewing about:home in a new window. - */ -add_task(function*(){ - let homepage = "about:home"; - yield SpecialPowers.pushPrefEnv({ - "set": [ - ["browser.startup.homepage", homepage], - ["browser.startup.page", 1], - ] - }); - - let win = OpenBrowserWindow(); - yield BrowserTestUtils.waitForEvent(win, "load"); - - let browser = win.gBrowser.selectedBrowser; - // If we've finished loading about:home already, we can check - // right away - otherwise, we need to wait. - if (browser.contentDocument.readyState == "complete" && - browser.currentURI.spec == homepage) { - checkIdentityMode(win); - } else { - yield BrowserTestUtils.browserLoaded(browser, false, homepage); - checkIdentityMode(win); - } - - yield BrowserTestUtils.closeWindow(win); -}); - -function checkIdentityMode(win) { - let identityMode = win.document.getElementById("identity-box").className; - is(identityMode, "chromeUI", "Identity state should be chromeUI for about:home in a new window"); -} +"use strict"; + +/** + * Tests that the identity-box shows the chromeUI styling + * when viewing about:home in a new window. + */ +add_task(function*(){ + let homepage = "about:home"; + yield SpecialPowers.pushPrefEnv({ + "set": [ + ["browser.startup.homepage", homepage], + ["browser.startup.page", 1], + ] + }); + + let win = OpenBrowserWindow(); + yield BrowserTestUtils.waitForEvent(win, "load"); + + let browser = win.gBrowser.selectedBrowser; + // If we've finished loading about:home already, we can check + // right away - otherwise, we need to wait. + if (browser.contentDocument.readyState == "complete" && + browser.currentURI.spec == homepage) { + checkIdentityMode(win); + } else { + yield BrowserTestUtils.browserLoaded(browser, false, homepage); + checkIdentityMode(win); + } + + yield BrowserTestUtils.closeWindow(win); +}); + +function checkIdentityMode(win) { + let identityMode = win.document.getElementById("identity-box").className; + is(identityMode, "chromeUI", "Identity state should be chromeUI for about:home in a new window"); +}
--- a/browser/base/content/test/general/browser_contextmenu.js +++ b/browser/base/content/test/general/browser_contextmenu.js @@ -1,879 +1,879 @@ -"use strict"; - -let contextMenu; -let LOGIN_FILL_ITEMS = [ - "---", null, - "fill-login", null, - [ - "fill-login-no-logins", false, - "---", null, - "fill-login-saved-passwords", true - ], null, -]; - -let hasPocket = Services.prefs.getBoolPref("extensions.pocket.enabled"); -let hasContainers = Services.prefs.getBoolPref("privacy.userContext.enabled"); - -add_task(function* test_setup() { - const example_base = "http://example.com/browser/browser/base/content/test/general/"; - const url = example_base + "subtst_contextmenu.html"; - yield BrowserTestUtils.openNewForegroundTab(gBrowser, url); - - const chrome_base = "chrome://mochitests/content/browser/browser/base/content/test/general/"; - const contextmenu_common = chrome_base + "contextmenu_common.js"; - Services.scriptloader.loadSubScript(contextmenu_common, this); - - yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { - let doc = content.document; - let videoIframe = doc.querySelector("#test-video-in-iframe"); - let video = videoIframe.contentDocument.querySelector("video"); - let awaitPause = ContentTaskUtils.waitForEvent(video, "pause"); - video.pause(); - yield awaitPause; - - let audioIframe = doc.querySelector("#test-audio-in-iframe"); - // media documents always use a <video> tag. - let audio = audioIframe.contentDocument.querySelector("video"); - awaitPause = ContentTaskUtils.waitForEvent(audio, "pause"); - audio.pause(); - yield awaitPause; - }); -}); - -let plainTextItems; -add_task(function* test_plaintext() { - plainTextItems = ["context-navigation", null, - ["context-back", false, - "context-forward", false, - "context-reload", true, - "context-bookmarkpage", true], null, - "---", null, - "context-savepage", true, - ...(hasPocket ? ["context-pocket", true] : []), - "---", null, - "context-viewbgimage", false, - "context-selectall", true, - "---", null, - "context-viewsource", true, - "context-viewinfo", true - ]; - yield test_contextmenu("#test-text", plainTextItems); -}); - -add_task(function* test_link() { - yield test_contextmenu("#test-link", - ["context-openlinkintab", true, - ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []), - // We need a blank entry here because the containers submenu is - // dynamically generated with no ids. - ...(hasContainers ? ["", null] : []), - "context-openlink", true, - "context-openlinkprivate", true, - "---", null, - "context-bookmarklink", true, - "context-savelink", true, - ...(hasPocket ? ["context-savelinktopocket", true] : []), - "context-copylink", true, - "context-searchselect", true - ] - ); -}); - -add_task(function* test_mailto() { - yield test_contextmenu("#test-mailto", - ["context-copyemail", true, - "context-searchselect", true - ] - ); -}); - -add_task(function* test_image() { - yield test_contextmenu("#test-image", - ["context-viewimage", true, - "context-copyimage-contents", true, - "context-copyimage", true, - "---", null, - "context-saveimage", true, - "context-sendimage", true, - "context-setDesktopBackground", true, - "context-viewimageinfo", true - ] - ); -}); - -add_task(function* test_canvas() { - yield test_contextmenu("#test-canvas", - ["context-viewimage", true, - "context-saveimage", true, - "context-selectall", true - ] - ); -}); - -add_task(function* test_video_ok() { - yield test_contextmenu("#test-video-ok", - ["context-media-play", true, - "context-media-mute", true, - "context-media-playbackrate", null, - ["context-media-playbackrate-050x", true, - "context-media-playbackrate-100x", true, - "context-media-playbackrate-125x", true, - "context-media-playbackrate-150x", true, - "context-media-playbackrate-200x", true], null, - "context-media-loop", true, - "context-media-hidecontrols", true, - "context-video-fullscreen", true, - "---", null, - "context-viewvideo", true, - "context-copyvideourl", true, - "---", null, - "context-savevideo", true, - "context-video-saveimage", true, - "context-sendvideo", true, - "context-castvideo", null, - [], null - ] - ); -}); - -add_task(function* test_audio_in_video() { - yield test_contextmenu("#test-audio-in-video", - ["context-media-play", true, - "context-media-mute", true, - "context-media-playbackrate", null, - ["context-media-playbackrate-050x", true, - "context-media-playbackrate-100x", true, - "context-media-playbackrate-125x", true, - "context-media-playbackrate-150x", true, - "context-media-playbackrate-200x", true], null, - "context-media-loop", true, - "context-media-showcontrols", true, - "---", null, - "context-copyaudiourl", true, - "---", null, - "context-saveaudio", true, - "context-sendaudio", true - ] - ); -}); - -add_task(function* test_video_bad() { - yield test_contextmenu("#test-video-bad", - ["context-media-play", false, - "context-media-mute", false, - "context-media-playbackrate", null, - ["context-media-playbackrate-050x", false, - "context-media-playbackrate-100x", false, - "context-media-playbackrate-125x", false, - "context-media-playbackrate-150x", false, - "context-media-playbackrate-200x", false], null, - "context-media-loop", true, - "context-media-hidecontrols", false, - "context-video-fullscreen", false, - "---", null, - "context-viewvideo", true, - "context-copyvideourl", true, - "---", null, - "context-savevideo", true, - "context-video-saveimage", false, - "context-sendvideo", true, - "context-castvideo", null, - [], null - ] - ); -}); - -add_task(function* test_video_bad2() { - yield test_contextmenu("#test-video-bad2", - ["context-media-play", false, - "context-media-mute", false, - "context-media-playbackrate", null, - ["context-media-playbackrate-050x", false, - "context-media-playbackrate-100x", false, - "context-media-playbackrate-125x", false, - "context-media-playbackrate-150x", false, - "context-media-playbackrate-200x", false], null, - "context-media-loop", true, - "context-media-hidecontrols", false, - "context-video-fullscreen", false, - "---", null, - "context-viewvideo", false, - "context-copyvideourl", false, - "---", null, - "context-savevideo", false, - "context-video-saveimage", false, - "context-sendvideo", false, - "context-castvideo", null, - [], null - ] - ); -}); - -add_task(function* test_iframe() { - yield test_contextmenu("#test-iframe", - ["context-navigation", null, - ["context-back", false, - "context-forward", false, - "context-reload", true, - "context-bookmarkpage", true], null, - "---", null, - "context-savepage", true, - ...(hasPocket ? ["context-pocket", true] : []), - "---", null, - "context-viewbgimage", false, - "context-selectall", true, - "frame", null, - ["context-showonlythisframe", true, - "context-openframeintab", true, - "context-openframe", true, - "---", null, - "context-reloadframe", true, - "---", null, - "context-bookmarkframe", true, - "context-saveframe", true, - "---", null, - "context-printframe", true, - "---", null, - "context-viewframesource", true, - "context-viewframeinfo", true], null, - "---", null, - "context-viewsource", true, - "context-viewinfo", true - ] - ); -}); - -add_task(function* test_video_in_iframe() { - yield test_contextmenu("#test-video-in-iframe", - ["context-media-play", true, - "context-media-mute", true, - "context-media-playbackrate", null, - ["context-media-playbackrate-050x", true, - "context-media-playbackrate-100x", true, - "context-media-playbackrate-125x", true, - "context-media-playbackrate-150x", true, - "context-media-playbackrate-200x", true], null, - "context-media-loop", true, - "context-media-hidecontrols", true, - "context-video-fullscreen", true, - "---", null, - "context-viewvideo", true, - "context-copyvideourl", true, - "---", null, - "context-savevideo", true, - "context-video-saveimage", true, - "context-sendvideo", true, - "context-castvideo", null, - [], null, - "frame", null, - ["context-showonlythisframe", true, - "context-openframeintab", true, - "context-openframe", true, - "---", null, - "context-reloadframe", true, - "---", null, - "context-bookmarkframe", true, - "context-saveframe", true, - "---", null, - "context-printframe", true, - "---", null, - "context-viewframeinfo", true], null] - ); -}); - -add_task(function* test_audio_in_iframe() { - yield test_contextmenu("#test-audio-in-iframe", - ["context-media-play", true, - "context-media-mute", true, - "context-media-playbackrate", null, - ["context-media-playbackrate-050x", true, - "context-media-playbackrate-100x", true, - "context-media-playbackrate-125x", true, - "context-media-playbackrate-150x", true, - "context-media-playbackrate-200x", true], null, - "context-media-loop", true, - "---", null, - "context-copyaudiourl", true, - "---", null, - "context-saveaudio", true, - "context-sendaudio", true, - "frame", null, - ["context-showonlythisframe", true, - "context-openframeintab", true, - "context-openframe", true, - "---", null, - "context-reloadframe", true, - "---", null, - "context-bookmarkframe", true, - "context-saveframe", true, - "---", null, - "context-printframe", true, - "---", null, - "context-viewframeinfo", true], null] - ); -}); - -add_task(function* test_image_in_iframe() { - yield test_contextmenu("#test-image-in-iframe", - ["context-viewimage", true, - "context-copyimage-contents", true, - "context-copyimage", true, - "---", null, - "context-saveimage", true, - "context-sendimage", true, - "context-setDesktopBackground", true, - "context-viewimageinfo", true, - "frame", null, - ["context-showonlythisframe", true, - "context-openframeintab", true, - "context-openframe", true, - "---", null, - "context-reloadframe", true, - "---", null, - "context-bookmarkframe", true, - "context-saveframe", true, - "---", null, - "context-printframe", true, - "---", null, - "context-viewframeinfo", true], null] - ); -}); - -add_task(function* test_textarea() { - // Disabled since this is seeing spell-check-enabled - // instead of spell-add-dictionaries-main - todo(false, "spell checker tests are failing, bug 1246296"); - return; - - yield test_contextmenu("#test-textarea", - ["context-undo", false, - "---", null, - "context-cut", true, - "context-copy", true, - "context-paste", null, - "context-delete", false, - "---", null, - "context-selectall", true, - "---", null, - "spell-add-dictionaries-main", true, - ], - { - skipFocusChange: true, - } - ); -}); - -add_task(function* test_textarea_spellcheck() { - todo(false, "spell checker tests are failing, bug 1246296"); - return; - - yield test_contextmenu("#test-textarea", - ["*chubbiness", true, // spelling suggestion - "spell-add-to-dictionary", true, - "---", null, - "context-undo", false, - "---", null, - "context-cut", true, - "context-copy", true, - "context-paste", null, // ignore clipboard state - "context-delete", false, - "---", null, - "context-selectall", true, - "---", null, - "spell-check-enabled", true, - "spell-dictionaries", true, - ["spell-check-dictionary-en-US", true, - "---", null, - "spell-add-dictionaries", true], null - ], - { - waitForSpellCheck: true, - offsetX: 6, - offsetY: 6, - postCheckContextMenuFn() { - document.getElementById("spell-add-to-dictionary").doCommand(); - } - } - ); -}); - -add_task(function* test_plaintext2() { - yield test_contextmenu("#test-text", plainTextItems); -}); - -add_task(function* test_undo_add_to_dictionary() { - todo(false, "spell checker tests are failing, bug 1246296"); - return; - - yield test_contextmenu("#test-textarea", - ["spell-undo-add-to-dictionary", true, - "---", null, - "context-undo", false, - "---", null, - "context-cut", true, - "context-copy", true, - "context-paste", null, // ignore clipboard state - "context-delete", false, - "---", null, - "context-selectall", true, - "---", null, - "spell-check-enabled", true, - "spell-dictionaries", true, - ["spell-check-dictionary-en-US", true, - "---", null, - "spell-add-dictionaries", true], null - ], - { - waitForSpellCheck: true, - postCheckContextMenuFn() { - document.getElementById("spell-undo-add-to-dictionary") - .doCommand(); - } - } - ); -}); - -add_task(function* test_contenteditable() { - todo(false, "spell checker tests are failing, bug 1246296"); - return; - - yield test_contextmenu("#test-contenteditable", - ["spell-no-suggestions", false, - "spell-add-to-dictionary", true, - "---", null, - "context-undo", false, - "---", null, - "context-cut", true, - "context-copy", true, - "context-paste", null, // ignore clipboard state - "context-delete", false, - "---", null, - "context-selectall", true, - "---", null, - "spell-check-enabled", true, - "spell-dictionaries", true, - ["spell-check-dictionary-en-US", true, - "---", null, - "spell-add-dictionaries", true], null - ], - {waitForSpellCheck: true} - ); -}); - -add_task(function* test_copylinkcommand() { - yield test_contextmenu("#test-link", null, { - postCheckContextMenuFn: function*() { - document.commandDispatcher - .getControllerForCommand("cmd_copyLink") - .doCommand("cmd_copyLink"); - - // The easiest way to check the clipboard is to paste the contents - // into a textbox. - yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { - let doc = content.document; - let input = doc.getElementById("test-input"); - input.focus(); - input.value = ""; - }); - document.commandDispatcher - .getControllerForCommand("cmd_paste") - .doCommand("cmd_paste"); - yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { - let doc = content.document; - let input = doc.getElementById("test-input"); - Assert.equal(input.value, "http://mozilla.com/", "paste for command cmd_paste"); - }); - } - }); -}); - -add_task(function* test_pagemenu() { - yield test_contextmenu("#test-pagemenu", - ["context-navigation", null, - ["context-back", false, - "context-forward", false, - "context-reload", true, - "context-bookmarkpage", true], null, - "---", null, - "+Plain item", {type: "", icon: "", checked: false, disabled: false}, - "+Disabled item", {type: "", icon: "", checked: false, disabled: true}, - "+Item w/ textContent", {type: "", icon: "", checked: false, disabled: false}, - "---", null, - "+Checkbox", {type: "checkbox", icon: "", checked: true, disabled: false}, - "---", null, - "+Radio1", {type: "checkbox", icon: "", checked: true, disabled: false}, - "+Radio2", {type: "checkbox", icon: "", checked: false, disabled: false}, - "+Radio3", {type: "checkbox", icon: "", checked: false, disabled: false}, - "---", null, - "+Item w/ icon", {type: "", icon: "favicon.ico", checked: false, disabled: false}, - "+Item w/ bad icon", {type: "", icon: "", checked: false, disabled: false}, - "---", null, - "generated-submenu-1", true, - ["+Radio1", {type: "checkbox", icon: "", checked: false, disabled: false}, - "+Radio2", {type: "checkbox", icon: "", checked: true, disabled: false}, - "+Radio3", {type: "checkbox", icon: "", checked: false, disabled: false}, - "---", null, - "+Checkbox", {type: "checkbox", icon: "", checked: false, disabled: false}], null, - "---", null, - "context-savepage", true, - ...(hasPocket ? ["context-pocket", true] : []), - "---", null, - "context-viewbgimage", false, - "context-selectall", true, - "---", null, - "context-viewsource", true, - "context-viewinfo", true - ], - {postCheckContextMenuFn: function*() { - let item = contextMenu.getElementsByAttribute("generateditemid", "1")[0]; - ok(item, "Got generated XUL menu item"); - item.doCommand(); - yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { - let pagemenu = content.document.getElementById("test-pagemenu"); - Assert.ok(!pagemenu.hasAttribute("hopeless"), "attribute got removed"); - }); - } - }); -}); - -add_task(function* test_dom_full_screen() { - yield test_contextmenu("#test-dom-full-screen", - ["context-navigation", null, - ["context-back", false, - "context-forward", false, - "context-reload", true, - "context-bookmarkpage", true], null, - "---", null, - "context-leave-dom-fullscreen", true, - "---", null, - "context-savepage", true, - ...(hasPocket ? ["context-pocket", true] : []), - "---", null, - "context-viewbgimage", false, - "context-selectall", true, - "---", null, - "context-viewsource", true, - "context-viewinfo", true - ], - { - shiftkey: true, - *preCheckContextMenuFn() { - yield pushPrefs(["full-screen-api.allow-trusted-requests-only", false], - ["full-screen-api.transition-duration.enter", "0 0"], - ["full-screen-api.transition-duration.leave", "0 0"]) - yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { - let doc = content.document; - let win = doc.defaultView; - let full_screen_element = doc.getElementById("test-dom-full-screen"); - let awaitFullScreenChange = - ContentTaskUtils.waitForEvent(win, "fullscreenchange"); - full_screen_element.requestFullscreen(); - yield awaitFullScreenChange; - }); - }, - *postCheckContextMenuFn() { - yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { - let win = content.document.defaultView; - let awaitFullScreenChange = - ContentTaskUtils.waitForEvent(win, "fullscreenchange"); - content.document.exitFullscreen(); - yield awaitFullScreenChange; - }); - } - } - ); -}); - -add_task(function* test_pagemenu2() { - yield test_contextmenu("#test-text", - ["context-navigation", null, - ["context-back", false, - "context-forward", false, - "context-reload", true, - "context-bookmarkpage", true], null, - "---", null, - "context-savepage", true, - ...(hasPocket ? ["context-pocket", true] : []), - "---", null, - "context-viewbgimage", false, - "context-selectall", true, - "---", null, - "context-viewsource", true, - "context-viewinfo", true - ], - {shiftkey: true} - ); -}); - -add_task(function* test_select_text() { - yield test_contextmenu("#test-select-text", - ["context-copy", true, - "context-selectall", true, - "---", null, - "context-searchselect", true, - "context-viewpartialsource-selection", true - ], - { - offsetX: 6, - offsetY: 6, - *preCheckContextMenuFn() { - yield selectText("#test-select-text"); - } - } - ); -}); - -add_task(function* test_select_text_link() { - yield test_contextmenu("#test-select-text-link", - ["context-openlinkincurrent", true, - "context-openlinkintab", true, - ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []), - // We need a blank entry here because the containers submenu is - // dynamically generated with no ids. - ...(hasContainers ? ["", null] : []), - "context-openlink", true, - "context-openlinkprivate", true, - "---", null, - "context-bookmarklink", true, - "context-savelink", true, - "context-copy", true, - "context-selectall", true, - "---", null, - "context-searchselect", true, - "context-viewpartialsource-selection", true - ], - { - offsetX: 6, - offsetY: 6, - *preCheckContextMenuFn() { - yield selectText("#test-select-text-link"); - }, - *postCheckContextMenuFn() { - yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { - let win = content.document.defaultView; - win.getSelection().removeAllRanges(); - }); - } - } - ); -}); - -add_task(function* test_imagelink() { - yield test_contextmenu("#test-image-link", - ["context-openlinkintab", true, - ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []), - // We need a blank entry here because the containers submenu is - // dynamically generated with no ids. - ...(hasContainers ? ["", null] : []), - "context-openlink", true, - "context-openlinkprivate", true, - "---", null, - "context-bookmarklink", true, - "context-savelink", true, - ...(hasPocket ? ["context-savelinktopocket", true] : []), - "context-copylink", true, - "---", null, - "context-viewimage", true, - "context-copyimage-contents", true, - "context-copyimage", true, - "---", null, - "context-saveimage", true, - "context-sendimage", true, - "context-setDesktopBackground", true, - "context-viewimageinfo", true - ] - ); -}); - -add_task(function* test_select_input_text() { - todo(false, "spell checker tests are failing, bug 1246296"); - return; - - yield test_contextmenu("#test-select-input-text", - ["context-undo", false, - "---", null, - "context-cut", true, - "context-copy", true, - "context-paste", null, // ignore clipboard state - "context-delete", true, - "---", null, - "context-selectall", true, - "context-searchselect",true, - "---", null, - "spell-check-enabled", true - ].concat(LOGIN_FILL_ITEMS), - { - *preCheckContextMenuFn() { - yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { - let doc = content.document; - let win = doc.defaultView; - win.getSelection().removeAllRanges(); - let element = doc.querySelector("#test-select-input-text"); - element.select(); - }); - } - } - ); -}); - -add_task(function* test_select_input_text_password() { - todo(false, "spell checker tests are failing, bug 1246296"); - return; - - yield test_contextmenu("#test-select-input-text-type-password", - ["context-undo", false, - "---", null, - "context-cut", true, - "context-copy", true, - "context-paste", null, // ignore clipboard state - "context-delete", true, - "---", null, - "context-selectall", true, - "---", null, - "spell-check-enabled", true, - //spell checker is shown on input[type="password"] on this testcase - "spell-dictionaries", true, - ["spell-check-dictionary-en-US", true, - "---", null, - "spell-add-dictionaries", true], null - ].concat(LOGIN_FILL_ITEMS), - { - *preCheckContextMenuFn() { - yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { - let doc = content.document; - let win = doc.defaultView; - win.getSelection().removeAllRanges(); - let element = doc.querySelector("#test-select-input-text-type-password"); - element.select(); - }); - }, - *postCheckContextMenuFn() { - yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { - let win = content.document.defaultView; - win.getSelection().removeAllRanges(); - }); - } - } - ); -}); - -add_task(function* test_click_to_play_blocked_plugin() { - yield test_contextmenu("#test-plugin", - ["context-navigation", null, - ["context-back", false, - "context-forward", false, - "context-reload", true, - "context-bookmarkpage", true], null, - "---", null, - "context-ctp-play", true, - "context-ctp-hide", true, - "---", null, - "context-savepage", true, - ...(hasPocket ? ["context-pocket", true] : []), - "---", null, - "context-viewbgimage", false, - "context-selectall", true, - "---", null, - "context-viewsource", true, - "context-viewinfo", true - ], - { - preCheckContextMenuFn: function*() { - pushPrefs(["plugins.click_to_play", true]); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - }, - postCheckContextMenuFn: function*() { - getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED; - } - } - ); -}); - -add_task(function* test_longdesc() { - yield test_contextmenu("#test-longdesc", - ["context-viewimage", true, - "context-copyimage-contents", true, - "context-copyimage", true, - "---", null, - "context-saveimage", true, - "context-sendimage", true, - "context-setDesktopBackground", true, - "context-viewimageinfo", true, - "context-viewimagedesc", true - ] - ); -}); - -add_task(function* test_srcdoc() { - yield test_contextmenu("#test-srcdoc", - ["context-navigation", null, - ["context-back", false, - "context-forward", false, - "context-reload", true, - "context-bookmarkpage", true], null, - "---", null, - "context-savepage", true, - ...(hasPocket ? ["context-pocket", true] : []), - "---", null, - "context-viewbgimage", false, - "context-selectall", true, - "frame", null, - ["context-reloadframe", true, - "---", null, - "context-saveframe", true, - "---", null, - "context-printframe", true, - "---", null, - "context-viewframesource", true, - "context-viewframeinfo", true], null, - "---", null, - "context-viewsource", true, - "context-viewinfo", true - ] - ); -}); - -add_task(function* test_input_spell_false() { - todo(false, "spell checker tests are failing, bug 1246296"); - return; - - yield test_contextmenu("#test-contenteditable-spellcheck-false", - ["context-undo", false, - "---", null, - "context-cut", true, - "context-copy", true, - "context-paste", null, // ignore clipboard state - "context-delete", false, - "---", null, - "context-selectall", true, - "---", null, - "spell-add-dictionaries-main", true, - ] - ); -}); - -add_task(function* test_cleanup() { - gBrowser.removeCurrentTab(); -}); - -/** - * Selects the text of the element that matches the provided `selector` - * - * @param {String} selector - * A selector passed to querySelector to find - * the element that will be referenced. - */ -function* selectText(selector) { - yield ContentTask.spawn(gBrowser.selectedBrowser, selector, function*(selector) { - info(`Selecting text of ${selector}`); - let doc = content.document; - let win = doc.defaultView; - win.getSelection().removeAllRanges(); - let div = doc.createRange(); - let element = doc.querySelector(selector); - Assert.ok(element, "Found element to select text from"); - div.setStartBefore(element); - div.setEndAfter(element); - win.getSelection().addRange(div); - }); -} +"use strict"; + +let contextMenu; +let LOGIN_FILL_ITEMS = [ + "---", null, + "fill-login", null, + [ + "fill-login-no-logins", false, + "---", null, + "fill-login-saved-passwords", true + ], null, +]; + +let hasPocket = Services.prefs.getBoolPref("extensions.pocket.enabled"); +let hasContainers = Services.prefs.getBoolPref("privacy.userContext.enabled"); + +add_task(function* test_setup() { + const example_base = "http://example.com/browser/browser/base/content/test/general/"; + const url = example_base + "subtst_contextmenu.html"; + yield BrowserTestUtils.openNewForegroundTab(gBrowser, url); + + const chrome_base = "chrome://mochitests/content/browser/browser/base/content/test/general/"; + const contextmenu_common = chrome_base + "contextmenu_common.js"; + Services.scriptloader.loadSubScript(contextmenu_common, this); + + yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { + let doc = content.document; + let videoIframe = doc.querySelector("#test-video-in-iframe"); + let video = videoIframe.contentDocument.querySelector("video"); + let awaitPause = ContentTaskUtils.waitForEvent(video, "pause"); + video.pause(); + yield awaitPause; + + let audioIframe = doc.querySelector("#test-audio-in-iframe"); + // media documents always use a <video> tag. + let audio = audioIframe.contentDocument.querySelector("video"); + awaitPause = ContentTaskUtils.waitForEvent(audio, "pause"); + audio.pause(); + yield awaitPause; + }); +}); + +let plainTextItems; +add_task(function* test_plaintext() { + plainTextItems = ["context-navigation", null, + ["context-back", false, + "context-forward", false, + "context-reload", true, + "context-bookmarkpage", true], null, + "---", null, + "context-savepage", true, + ...(hasPocket ? ["context-pocket", true] : []), + "---", null, + "context-viewbgimage", false, + "context-selectall", true, + "---", null, + "context-viewsource", true, + "context-viewinfo", true + ]; + yield test_contextmenu("#test-text", plainTextItems); +}); + +add_task(function* test_link() { + yield test_contextmenu("#test-link", + ["context-openlinkintab", true, + ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []), + // We need a blank entry here because the containers submenu is + // dynamically generated with no ids. + ...(hasContainers ? ["", null] : []), + "context-openlink", true, + "context-openlinkprivate", true, + "---", null, + "context-bookmarklink", true, + "context-savelink", true, + ...(hasPocket ? ["context-savelinktopocket", true] : []), + "context-copylink", true, + "context-searchselect", true + ] + ); +}); + +add_task(function* test_mailto() { + yield test_contextmenu("#test-mailto", + ["context-copyemail", true, + "context-searchselect", true + ] + ); +}); + +add_task(function* test_image() { + yield test_contextmenu("#test-image", + ["context-viewimage", true, + "context-copyimage-contents", true, + "context-copyimage", true, + "---", null, + "context-saveimage", true, + "context-sendimage", true, + "context-setDesktopBackground", true, + "context-viewimageinfo", true + ] + ); +}); + +add_task(function* test_canvas() { + yield test_contextmenu("#test-canvas", + ["context-viewimage", true, + "context-saveimage", true, + "context-selectall", true + ] + ); +}); + +add_task(function* test_video_ok() { + yield test_contextmenu("#test-video-ok", + ["context-media-play", true, + "context-media-mute", true, + "context-media-playbackrate", null, + ["context-media-playbackrate-050x", true, + "context-media-playbackrate-100x", true, + "context-media-playbackrate-125x", true, + "context-media-playbackrate-150x", true, + "context-media-playbackrate-200x", true], null, + "context-media-loop", true, + "context-media-hidecontrols", true, + "context-video-fullscreen", true, + "---", null, + "context-viewvideo", true, + "context-copyvideourl", true, + "---", null, + "context-savevideo", true, + "context-video-saveimage", true, + "context-sendvideo", true, + "context-castvideo", null, + [], null + ] + ); +}); + +add_task(function* test_audio_in_video() { + yield test_contextmenu("#test-audio-in-video", + ["context-media-play", true, + "context-media-mute", true, + "context-media-playbackrate", null, + ["context-media-playbackrate-050x", true, + "context-media-playbackrate-100x", true, + "context-media-playbackrate-125x", true, + "context-media-playbackrate-150x", true, + "context-media-playbackrate-200x", true], null, + "context-media-loop", true, + "context-media-showcontrols", true, + "---", null, + "context-copyaudiourl", true, + "---", null, + "context-saveaudio", true, + "context-sendaudio", true + ] + ); +}); + +add_task(function* test_video_bad() { + yield test_contextmenu("#test-video-bad", + ["context-media-play", false, + "context-media-mute", false, + "context-media-playbackrate", null, + ["context-media-playbackrate-050x", false, + "context-media-playbackrate-100x", false, + "context-media-playbackrate-125x", false, + "context-media-playbackrate-150x", false, + "context-media-playbackrate-200x", false], null, + "context-media-loop", true, + "context-media-hidecontrols", false, + "context-video-fullscreen", false, + "---", null, + "context-viewvideo", true, + "context-copyvideourl", true, + "---", null, + "context-savevideo", true, + "context-video-saveimage", false, + "context-sendvideo", true, + "context-castvideo", null, + [], null + ] + ); +}); + +add_task(function* test_video_bad2() { + yield test_contextmenu("#test-video-bad2", + ["context-media-play", false, + "context-media-mute", false, + "context-media-playbackrate", null, + ["context-media-playbackrate-050x", false, + "context-media-playbackrate-100x", false, + "context-media-playbackrate-125x", false, + "context-media-playbackrate-150x", false, + "context-media-playbackrate-200x", false], null, + "context-media-loop", true, + "context-media-hidecontrols", false, + "context-video-fullscreen", false, + "---", null, + "context-viewvideo", false, + "context-copyvideourl", false, + "---", null, + "context-savevideo", false, + "context-video-saveimage", false, + "context-sendvideo", false, + "context-castvideo", null, + [], null + ] + ); +}); + +add_task(function* test_iframe() { + yield test_contextmenu("#test-iframe", + ["context-navigation", null, + ["context-back", false, + "context-forward", false, + "context-reload", true, + "context-bookmarkpage", true], null, + "---", null, + "context-savepage", true, + ...(hasPocket ? ["context-pocket", true] : []), + "---", null, + "context-viewbgimage", false, + "context-selectall", true, + "frame", null, + ["context-showonlythisframe", true, + "context-openframeintab", true, + "context-openframe", true, + "---", null, + "context-reloadframe", true, + "---", null, + "context-bookmarkframe", true, + "context-saveframe", true, + "---", null, + "context-printframe", true, + "---", null, + "context-viewframesource", true, + "context-viewframeinfo", true], null, + "---", null, + "context-viewsource", true, + "context-viewinfo", true + ] + ); +}); + +add_task(function* test_video_in_iframe() { + yield test_contextmenu("#test-video-in-iframe", + ["context-media-play", true, + "context-media-mute", true, + "context-media-playbackrate", null, + ["context-media-playbackrate-050x", true, + "context-media-playbackrate-100x", true, + "context-media-playbackrate-125x", true, + "context-media-playbackrate-150x", true, + "context-media-playbackrate-200x", true], null, + "context-media-loop", true, + "context-media-hidecontrols", true, + "context-video-fullscreen", true, + "---", null, + "context-viewvideo", true, + "context-copyvideourl", true, + "---", null, + "context-savevideo", true, + "context-video-saveimage", true, + "context-sendvideo", true, + "context-castvideo", null, + [], null, + "frame", null, + ["context-showonlythisframe", true, + "context-openframeintab", true, + "context-openframe", true, + "---", null, + "context-reloadframe", true, + "---", null, + "context-bookmarkframe", true, + "context-saveframe", true, + "---", null, + "context-printframe", true, + "---", null, + "context-viewframeinfo", true], null] + ); +}); + +add_task(function* test_audio_in_iframe() { + yield test_contextmenu("#test-audio-in-iframe", + ["context-media-play", true, + "context-media-mute", true, + "context-media-playbackrate", null, + ["context-media-playbackrate-050x", true, + "context-media-playbackrate-100x", true, + "context-media-playbackrate-125x", true, + "context-media-playbackrate-150x", true, + "context-media-playbackrate-200x", true], null, + "context-media-loop", true, + "---", null, + "context-copyaudiourl", true, + "---", null, + "context-saveaudio", true, + "context-sendaudio", true, + "frame", null, + ["context-showonlythisframe", true, + "context-openframeintab", true, + "context-openframe", true, + "---", null, + "context-reloadframe", true, + "---", null, + "context-bookmarkframe", true, + "context-saveframe", true, + "---", null, + "context-printframe", true, + "---", null, + "context-viewframeinfo", true], null] + ); +}); + +add_task(function* test_image_in_iframe() { + yield test_contextmenu("#test-image-in-iframe", + ["context-viewimage", true, + "context-copyimage-contents", true, + "context-copyimage", true, + "---", null, + "context-saveimage", true, + "context-sendimage", true, + "context-setDesktopBackground", true, + "context-viewimageinfo", true, + "frame", null, + ["context-showonlythisframe", true, + "context-openframeintab", true, + "context-openframe", true, + "---", null, + "context-reloadframe", true, + "---", null, + "context-bookmarkframe", true, + "context-saveframe", true, + "---", null, + "context-printframe", true, + "---", null, + "context-viewframeinfo", true], null] + ); +}); + +add_task(function* test_textarea() { + // Disabled since this is seeing spell-check-enabled + // instead of spell-add-dictionaries-main + todo(false, "spell checker tests are failing, bug 1246296"); + return; + + yield test_contextmenu("#test-textarea", + ["context-undo", false, + "---", null, + "context-cut", true, + "context-copy", true, + "context-paste", null, + "context-delete", false, + "---", null, + "context-selectall", true, + "---", null, + "spell-add-dictionaries-main", true, + ], + { + skipFocusChange: true, + } + ); +}); + +add_task(function* test_textarea_spellcheck() { + todo(false, "spell checker tests are failing, bug 1246296"); + return; + + yield test_contextmenu("#test-textarea", + ["*chubbiness", true, // spelling suggestion + "spell-add-to-dictionary", true, + "---", null, + "context-undo", false, + "---", null, + "context-cut", true, + "context-copy", true, + "context-paste", null, // ignore clipboard state + "context-delete", false, + "---", null, + "context-selectall", true, + "---", null, + "spell-check-enabled", true, + "spell-dictionaries", true, + ["spell-check-dictionary-en-US", true, + "---", null, + "spell-add-dictionaries", true], null + ], + { + waitForSpellCheck: true, + offsetX: 6, + offsetY: 6, + postCheckContextMenuFn() { + document.getElementById("spell-add-to-dictionary").doCommand(); + } + } + ); +}); + +add_task(function* test_plaintext2() { + yield test_contextmenu("#test-text", plainTextItems); +}); + +add_task(function* test_undo_add_to_dictionary() { + todo(false, "spell checker tests are failing, bug 1246296"); + return; + + yield test_contextmenu("#test-textarea", + ["spell-undo-add-to-dictionary", true, + "---", null, + "context-undo", false, + "---", null, + "context-cut", true, + "context-copy", true, + "context-paste", null, // ignore clipboard state + "context-delete", false, + "---", null, + "context-selectall", true, + "---", null, + "spell-check-enabled", true, + "spell-dictionaries", true, + ["spell-check-dictionary-en-US", true, + "---", null, + "spell-add-dictionaries", true], null + ], + { + waitForSpellCheck: true, + postCheckContextMenuFn() { + document.getElementById("spell-undo-add-to-dictionary") + .doCommand(); + } + } + ); +}); + +add_task(function* test_contenteditable() { + todo(false, "spell checker tests are failing, bug 1246296"); + return; + + yield test_contextmenu("#test-contenteditable", + ["spell-no-suggestions", false, + "spell-add-to-dictionary", true, + "---", null, + "context-undo", false, + "---", null, + "context-cut", true, + "context-copy", true, + "context-paste", null, // ignore clipboard state + "context-delete", false, + "---", null, + "context-selectall", true, + "---", null, + "spell-check-enabled", true, + "spell-dictionaries", true, + ["spell-check-dictionary-en-US", true, + "---", null, + "spell-add-dictionaries", true], null + ], + {waitForSpellCheck: true} + ); +}); + +add_task(function* test_copylinkcommand() { + yield test_contextmenu("#test-link", null, { + postCheckContextMenuFn: function*() { + document.commandDispatcher + .getControllerForCommand("cmd_copyLink") + .doCommand("cmd_copyLink"); + + // The easiest way to check the clipboard is to paste the contents + // into a textbox. + yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { + let doc = content.document; + let input = doc.getElementById("test-input"); + input.focus(); + input.value = ""; + }); + document.commandDispatcher + .getControllerForCommand("cmd_paste") + .doCommand("cmd_paste"); + yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { + let doc = content.document; + let input = doc.getElementById("test-input"); + Assert.equal(input.value, "http://mozilla.com/", "paste for command cmd_paste"); + }); + } + }); +}); + +add_task(function* test_pagemenu() { + yield test_contextmenu("#test-pagemenu", + ["context-navigation", null, + ["context-back", false, + "context-forward", false, + "context-reload", true, + "context-bookmarkpage", true], null, + "---", null, + "+Plain item", {type: "", icon: "", checked: false, disabled: false}, + "+Disabled item", {type: "", icon: "", checked: false, disabled: true}, + "+Item w/ textContent", {type: "", icon: "", checked: false, disabled: false}, + "---", null, + "+Checkbox", {type: "checkbox", icon: "", checked: true, disabled: false}, + "---", null, + "+Radio1", {type: "checkbox", icon: "", checked: true, disabled: false}, + "+Radio2", {type: "checkbox", icon: "", checked: false, disabled: false}, + "+Radio3", {type: "checkbox", icon: "", checked: false, disabled: false}, + "---", null, + "+Item w/ icon", {type: "", icon: "favicon.ico", checked: false, disabled: false}, + "+Item w/ bad icon", {type: "", icon: "", checked: false, disabled: false}, + "---", null, + "generated-submenu-1", true, + ["+Radio1", {type: "checkbox", icon: "", checked: false, disabled: false}, + "+Radio2", {type: "checkbox", icon: "", checked: true, disabled: false}, + "+Radio3", {type: "checkbox", icon: "", checked: false, disabled: false}, + "---", null, + "+Checkbox", {type: "checkbox", icon: "", checked: false, disabled: false}], null, + "---", null, + "context-savepage", true, + ...(hasPocket ? ["context-pocket", true] : []), + "---", null, + "context-viewbgimage", false, + "context-selectall", true, + "---", null, + "context-viewsource", true, + "context-viewinfo", true + ], + {postCheckContextMenuFn: function*() { + let item = contextMenu.getElementsByAttribute("generateditemid", "1")[0]; + ok(item, "Got generated XUL menu item"); + item.doCommand(); + yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { + let pagemenu = content.document.getElementById("test-pagemenu"); + Assert.ok(!pagemenu.hasAttribute("hopeless"), "attribute got removed"); + }); + } + }); +}); + +add_task(function* test_dom_full_screen() { + yield test_contextmenu("#test-dom-full-screen", + ["context-navigation", null, + ["context-back", false, + "context-forward", false, + "context-reload", true, + "context-bookmarkpage", true], null, + "---", null, + "context-leave-dom-fullscreen", true, + "---", null, + "context-savepage", true, + ...(hasPocket ? ["context-pocket", true] : []), + "---", null, + "context-viewbgimage", false, + "context-selectall", true, + "---", null, + "context-viewsource", true, + "context-viewinfo", true + ], + { + shiftkey: true, + *preCheckContextMenuFn() { + yield pushPrefs(["full-screen-api.allow-trusted-requests-only", false], + ["full-screen-api.transition-duration.enter", "0 0"], + ["full-screen-api.transition-duration.leave", "0 0"]) + yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { + let doc = content.document; + let win = doc.defaultView; + let full_screen_element = doc.getElementById("test-dom-full-screen"); + let awaitFullScreenChange = + ContentTaskUtils.waitForEvent(win, "fullscreenchange"); + full_screen_element.requestFullscreen(); + yield awaitFullScreenChange; + }); + }, + *postCheckContextMenuFn() { + yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { + let win = content.document.defaultView; + let awaitFullScreenChange = + ContentTaskUtils.waitForEvent(win, "fullscreenchange"); + content.document.exitFullscreen(); + yield awaitFullScreenChange; + }); + } + } + ); +}); + +add_task(function* test_pagemenu2() { + yield test_contextmenu("#test-text", + ["context-navigation", null, + ["context-back", false, + "context-forward", false, + "context-reload", true, + "context-bookmarkpage", true], null, + "---", null, + "context-savepage", true, + ...(hasPocket ? ["context-pocket", true] : []), + "---", null, + "context-viewbgimage", false, + "context-selectall", true, + "---", null, + "context-viewsource", true, + "context-viewinfo", true + ], + {shiftkey: true} + ); +}); + +add_task(function* test_select_text() { + yield test_contextmenu("#test-select-text", + ["context-copy", true, + "context-selectall", true, + "---", null, + "context-searchselect", true, + "context-viewpartialsource-selection", true + ], + { + offsetX: 6, + offsetY: 6, + *preCheckContextMenuFn() { + yield selectText("#test-select-text"); + } + } + ); +}); + +add_task(function* test_select_text_link() { + yield test_contextmenu("#test-select-text-link", + ["context-openlinkincurrent", true, + "context-openlinkintab", true, + ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []), + // We need a blank entry here because the containers submenu is + // dynamically generated with no ids. + ...(hasContainers ? ["", null] : []), + "context-openlink", true, + "context-openlinkprivate", true, + "---", null, + "context-bookmarklink", true, + "context-savelink", true, + "context-copy", true, + "context-selectall", true, + "---", null, + "context-searchselect", true, + "context-viewpartialsource-selection", true + ], + { + offsetX: 6, + offsetY: 6, + *preCheckContextMenuFn() { + yield selectText("#test-select-text-link"); + }, + *postCheckContextMenuFn() { + yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { + let win = content.document.defaultView; + win.getSelection().removeAllRanges(); + }); + } + } + ); +}); + +add_task(function* test_imagelink() { + yield test_contextmenu("#test-image-link", + ["context-openlinkintab", true, + ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []), + // We need a blank entry here because the containers submenu is + // dynamically generated with no ids. + ...(hasContainers ? ["", null] : []), + "context-openlink", true, + "context-openlinkprivate", true, + "---", null, + "context-bookmarklink", true, + "context-savelink", true, + ...(hasPocket ? ["context-savelinktopocket", true] : []), + "context-copylink", true, + "---", null, + "context-viewimage", true, + "context-copyimage-contents", true, + "context-copyimage", true, + "---", null, + "context-saveimage", true, + "context-sendimage", true, + "context-setDesktopBackground", true, + "context-viewimageinfo", true + ] + ); +}); + +add_task(function* test_select_input_text() { + todo(false, "spell checker tests are failing, bug 1246296"); + return; + + yield test_contextmenu("#test-select-input-text", + ["context-undo", false, + "---", null, + "context-cut", true, + "context-copy", true, + "context-paste", null, // ignore clipboard state + "context-delete", true, + "---", null, + "context-selectall", true, + "context-searchselect",true, + "---", null, + "spell-check-enabled", true + ].concat(LOGIN_FILL_ITEMS), + { + *preCheckContextMenuFn() { + yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { + let doc = content.document; + let win = doc.defaultView; + win.getSelection().removeAllRanges(); + let element = doc.querySelector("#test-select-input-text"); + element.select(); + }); + } + } + ); +}); + +add_task(function* test_select_input_text_password() { + todo(false, "spell checker tests are failing, bug 1246296"); + return; + + yield test_contextmenu("#test-select-input-text-type-password", + ["context-undo", false, + "---", null, + "context-cut", true, + "context-copy", true, + "context-paste", null, // ignore clipboard state + "context-delete", true, + "---", null, + "context-selectall", true, + "---", null, + "spell-check-enabled", true, + //spell checker is shown on input[type="password"] on this testcase + "spell-dictionaries", true, + ["spell-check-dictionary-en-US", true, + "---", null, + "spell-add-dictionaries", true], null + ].concat(LOGIN_FILL_ITEMS), + { + *preCheckContextMenuFn() { + yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { + let doc = content.document; + let win = doc.defaultView; + win.getSelection().removeAllRanges(); + let element = doc.querySelector("#test-select-input-text-type-password"); + element.select(); + }); + }, + *postCheckContextMenuFn() { + yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { + let win = content.document.defaultView; + win.getSelection().removeAllRanges(); + }); + } + } + ); +}); + +add_task(function* test_click_to_play_blocked_plugin() { + yield test_contextmenu("#test-plugin", + ["context-navigation", null, + ["context-back", false, + "context-forward", false, + "context-reload", true, + "context-bookmarkpage", true], null, + "---", null, + "context-ctp-play", true, + "context-ctp-hide", true, + "---", null, + "context-savepage", true, + ...(hasPocket ? ["context-pocket", true] : []), + "---", null, + "context-viewbgimage", false, + "context-selectall", true, + "---", null, + "context-viewsource", true, + "context-viewinfo", true + ], + { + preCheckContextMenuFn: function*() { + pushPrefs(["plugins.click_to_play", true]); + setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY); + }, + postCheckContextMenuFn: function*() { + getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED; + } + } + ); +}); + +add_task(function* test_longdesc() { + yield test_contextmenu("#test-longdesc", + ["context-viewimage", true, + "context-copyimage-contents", true, + "context-copyimage", true, + "---", null, + "context-saveimage", true, + "context-sendimage", true, + "context-setDesktopBackground", true, + "context-viewimageinfo", true, + "context-viewimagedesc", true + ] + ); +}); + +add_task(function* test_srcdoc() { + yield test_contextmenu("#test-srcdoc", + ["context-navigation", null, + ["context-back", false, + "context-forward", false, + "context-reload", true, + "context-bookmarkpage", true], null, + "---", null, + "context-savepage", true, + ...(hasPocket ? ["context-pocket", true] : []), + "---", null, + "context-viewbgimage", false, + "context-selectall", true, + "frame", null, + ["context-reloadframe", true, + "---", null, + "context-saveframe", true, + "---", null, + "context-printframe", true, + "---", null, + "context-viewframesource", true, + "context-viewframeinfo", true], null, + "---", null, + "context-viewsource", true, + "context-viewinfo", true + ] + ); +}); + +add_task(function* test_input_spell_false() { + todo(false, "spell checker tests are failing, bug 1246296"); + return; + + yield test_contextmenu("#test-contenteditable-spellcheck-false", + ["context-undo", false, + "---", null, + "context-cut", true, + "context-copy", true, + "context-paste", null, // ignore clipboard state + "context-delete", false, + "---", null, + "context-selectall", true, + "---", null, + "spell-add-dictionaries-main", true, + ] + ); +}); + +add_task(function* test_cleanup() { + gBrowser.removeCurrentTab(); +}); + +/** + * Selects the text of the element that matches the provided `selector` + * + * @param {String} selector + * A selector passed to querySelector to find + * the element that will be referenced. + */ +function* selectText(selector) { + yield ContentTask.spawn(gBrowser.selectedBrowser, selector, function*(selector) { + info(`Selecting text of ${selector}`); + let doc = content.document; + let win = doc.defaultView; + win.getSelection().removeAllRanges(); + let div = doc.createRange(); + let element = doc.querySelector(selector); + Assert.ok(element, "Found element to select text from"); + div.setStartBefore(element); + div.setEndAfter(element); + win.getSelection().addRange(div); + }); +}
--- a/browser/base/content/test/general/browser_decoderDoctor.js +++ b/browser/base/content/test/general/browser_decoderDoctor.js @@ -1,97 +1,97 @@ -"use strict"; - -function* test_decoder_doctor_notification(type, notificationMessage, options) { - yield BrowserTestUtils.withNewTab({ gBrowser }, function*(browser) { - let awaitNotificationBar = - BrowserTestUtils.waitForNotificationBar(gBrowser, browser, "decoder-doctor-notification"); - - yield ContentTask.spawn(browser, type, function*(type) { - Services.obs.notifyObservers(content.window, - "decoder-doctor-notification", - JSON.stringify({type: type, - isSolved: false, - decoderDoctorReportId: "test", - formats: "test"})); - }); - - let notification; - try { - notification = yield awaitNotificationBar; - } catch (ex) { - ok(false, ex); - return; - } - ok(notification, "Got decoder-doctor-notification notification"); - - is(notification.getAttribute("label"), notificationMessage, - "notification message should match expectation"); - let button = notification.childNodes[0]; - if (options && options.noLearnMoreButton) { - ok(!button, "There should not be a Learn More button"); - return; - } - - is(button.getAttribute("label"), gNavigatorBundle.getString("decoder.noCodecs.button"), - "notification button should be 'Learn more'"); - is(button.getAttribute("accesskey"), gNavigatorBundle.getString("decoder.noCodecs.accesskey"), - "notification button should have accesskey"); - - let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL"); - let url = baseURL + "fix-video-audio-problems-firefox-windows"; - let awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser, url); - button.click(); - let sumoTab = yield awaitNewTab; - yield BrowserTestUtils.removeTab(sumoTab); - }); -} - -add_task(function* test_adobe_cdm_not_found() { - // This is only sent on Windows. - if (AppConstants.platform != "win") { - return; - } - - let message; - if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) { - message = gNavigatorBundle.getFormattedString("emeNotifications.drmContentDisabled.message", [""]); - } else { - message = gNavigatorBundle.getString("decoder.noCodecs.message"); - } - - yield test_decoder_doctor_notification("adobe-cdm-not-found", message); -}); - -add_task(function* test_adobe_cdm_not_activated() { - // This is only sent on Windows. - if (AppConstants.platform != "win") { - return; - } - - let message; - if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) { - message = gNavigatorBundle.getString("decoder.noCodecsXP.message"); - } else { - message = gNavigatorBundle.getString("decoder.noCodecs.message"); - } - - yield test_decoder_doctor_notification("adobe-cdm-not-activated", message); -}); - -add_task(function* test_platform_decoder_not_found() { - // Not sent on Windows XP. - if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) { - return; - } - - let message; - let isLinux = AppConstants.platform == "linux"; - if (isLinux) { - message = gNavigatorBundle.getString("decoder.noCodecsLinux.message"); - } else { - message = gNavigatorBundle.getString("decoder.noHWAcceleration.message"); - } - - yield test_decoder_doctor_notification("platform-decoder-not-found", - message, - {noLearnMoreButton: isLinux}); -}); +"use strict"; + +function* test_decoder_doctor_notification(type, notificationMessage, options) { + yield BrowserTestUtils.withNewTab({ gBrowser }, function*(browser) { + let awaitNotificationBar = + BrowserTestUtils.waitForNotificationBar(gBrowser, browser, "decoder-doctor-notification"); + + yield ContentTask.spawn(browser, type, function*(type) { + Services.obs.notifyObservers(content.window, + "decoder-doctor-notification", + JSON.stringify({type: type, + isSolved: false, + decoderDoctorReportId: "test", + formats: "test"})); + }); + + let notification; + try { + notification = yield awaitNotificationBar; + } catch (ex) { + ok(false, ex); + return; + } + ok(notification, "Got decoder-doctor-notification notification"); + + is(notification.getAttribute("label"), notificationMessage, + "notification message should match expectation"); + let button = notification.childNodes[0]; + if (options && options.noLearnMoreButton) { + ok(!button, "There should not be a Learn More button"); + return; + } + + is(button.getAttribute("label"), gNavigatorBundle.getString("decoder.noCodecs.button"), + "notification button should be 'Learn more'"); + is(button.getAttribute("accesskey"), gNavigatorBundle.getString("decoder.noCodecs.accesskey"), + "notification button should have accesskey"); + + let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL"); + let url = baseURL + "fix-video-audio-problems-firefox-windows"; + let awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser, url); + button.click(); + let sumoTab = yield awaitNewTab; + yield BrowserTestUtils.removeTab(sumoTab); + }); +} + +add_task(function* test_adobe_cdm_not_found() { + // This is only sent on Windows. + if (AppConstants.platform != "win") { + return; + } + + let message; + if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) { + message = gNavigatorBundle.getFormattedString("emeNotifications.drmContentDisabled.message", [""]); + } else { + message = gNavigatorBundle.getString("decoder.noCodecs.message"); + } + + yield test_decoder_doctor_notification("adobe-cdm-not-found", message); +}); + +add_task(function* test_adobe_cdm_not_activated() { + // This is only sent on Windows. + if (AppConstants.platform != "win") { + return; + } + + let message; + if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) { + message = gNavigatorBundle.getString("decoder.noCodecsXP.message"); + } else { + message = gNavigatorBundle.getString("decoder.noCodecs.message"); + } + + yield test_decoder_doctor_notification("adobe-cdm-not-activated", message); +}); + +add_task(function* test_platform_decoder_not_found() { + // Not sent on Windows XP. + if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) { + return; + } + + let message; + let isLinux = AppConstants.platform == "linux"; + if (isLinux) { + message = gNavigatorBundle.getString("decoder.noCodecsLinux.message"); + } else { + message = gNavigatorBundle.getString("decoder.noHWAcceleration.message"); + } + + yield test_decoder_doctor_notification("platform-decoder-not-found", + message, + {noLearnMoreButton: isLinux}); +});
--- a/browser/base/content/test/general/browser_favicon_change.js +++ b/browser/base/content/test/general/browser_favicon_change.js @@ -1,40 +1,40 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -const TEST_URL = "http://mochi.test:8888/browser/browser/base/content/test/general/file_favicon_change.html" - -add_task(function*() { - let extraTab = gBrowser.selectedTab = gBrowser.addTab(); - let tabLoaded = promiseTabLoaded(extraTab); - extraTab.linkedBrowser.loadURI(TEST_URL); - let expectedFavicon = "http://example.org/one-icon"; - let haveChanged = new Promise.defer(); - let observer = new MutationObserver(function(mutations) { - for (let mut of mutations) { - if (mut.attributeName != "image") { - continue; - } - let imageVal = extraTab.getAttribute("image").replace(/#.*$/, ""); - if (!imageVal) { - // The value gets removed because it doesn't load. - continue; - } - is(imageVal, expectedFavicon, "Favicon image should correspond to expected image."); - haveChanged.resolve(); - } - }); - observer.observe(extraTab, {attributes: true}); - yield tabLoaded; - yield haveChanged.promise; - haveChanged = new Promise.defer(); - expectedFavicon = "http://example.org/other-icon"; - let contentWin = extraTab.linkedBrowser.contentWindow; - let ev = new contentWin.CustomEvent("PleaseChangeFavicon", {}); - contentWin.dispatchEvent(ev); - yield haveChanged.promise; - observer.disconnect(); - gBrowser.removeTab(extraTab); -}); - +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URL = "http://mochi.test:8888/browser/browser/base/content/test/general/file_favicon_change.html" + +add_task(function*() { + let extraTab = gBrowser.selectedTab = gBrowser.addTab(); + let tabLoaded = promiseTabLoaded(extraTab); + extraTab.linkedBrowser.loadURI(TEST_URL); + let expectedFavicon = "http://example.org/one-icon"; + let haveChanged = new Promise.defer(); + let observer = new MutationObserver(function(mutations) { + for (let mut of mutations) { + if (mut.attributeName != "image") { + continue; + } + let imageVal = extraTab.getAttribute("image").replace(/#.*$/, ""); + if (!imageVal) { + // The value gets removed because it doesn't load. + continue; + } + is(imageVal, expectedFavicon, "Favicon image should correspond to expected image."); + haveChanged.resolve(); + } + }); + observer.observe(extraTab, {attributes: true}); + yield tabLoaded; + yield haveChanged.promise; + haveChanged = new Promise.defer(); + expectedFavicon = "http://example.org/other-icon"; + let contentWin = extraTab.linkedBrowser.contentWindow; + let ev = new contentWin.CustomEvent("PleaseChangeFavicon", {}); + contentWin.dispatchEvent(ev); + yield haveChanged.promise; + observer.disconnect(); + gBrowser.removeTab(extraTab); +}); +
--- a/browser/base/content/test/general/browser_menuButtonFitts.js +++ b/browser/base/content/test/general/browser_menuButtonFitts.js @@ -1,32 +1,32 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -function test () { - waitForExplicitFinish(); - window.maximize(); - - // Find where the nav-bar is vertically. - var navBar = document.getElementById("nav-bar"); - var boundingRect = navBar.getBoundingClientRect(); - var yPixel = boundingRect.top + Math.floor(boundingRect.height / 2); - var xPixel = boundingRect.width - 1; // Use the last pixel of the screen since it is maximized. - - function onPopupHidden() { - PanelUI.panel.removeEventListener("popuphidden", onPopupHidden); - window.restore(); - finish(); - } - function onPopupShown() { - PanelUI.panel.removeEventListener("popupshown", onPopupShown); - ok(true, "Clicking at the far edge of the window opened the menu popup."); - PanelUI.panel.addEventListener("popuphidden", onPopupHidden); - PanelUI.hide(); - } - registerCleanupFunction(function() { - PanelUI.panel.removeEventListener("popupshown", onPopupShown); - PanelUI.panel.removeEventListener("popuphidden", onPopupHidden); - }); - PanelUI.panel.addEventListener("popupshown", onPopupShown); - EventUtils.synthesizeMouseAtPoint(xPixel, yPixel, {}, window); -} +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function test () { + waitForExplicitFinish(); + window.maximize(); + + // Find where the nav-bar is vertically. + var navBar = document.getElementById("nav-bar"); + var boundingRect = navBar.getBoundingClientRect(); + var yPixel = boundingRect.top + Math.floor(boundingRect.height / 2); + var xPixel = boundingRect.width - 1; // Use the last pixel of the screen since it is maximized. + + function onPopupHidden() { + PanelUI.panel.removeEventListener("popuphidden", onPopupHidden); + window.restore(); + finish(); + } + function onPopupShown() { + PanelUI.panel.removeEventListener("popupshown", onPopupShown); + ok(true, "Clicking at the far edge of the window opened the menu popup."); + PanelUI.panel.addEventListener("popuphidden", onPopupHidden); + PanelUI.hide(); + } + registerCleanupFunction(function() { + PanelUI.panel.removeEventListener("popupshown", onPopupShown); + PanelUI.panel.removeEventListener("popuphidden", onPopupHidden); + }); + PanelUI.panel.addEventListener("popupshown", onPopupShown); + EventUtils.synthesizeMouseAtPoint(xPixel, yPixel, {}, window); +}
--- a/browser/base/content/test/general/browser_remoteTroubleshoot.js +++ b/browser/base/content/test/general/browser_remoteTroubleshoot.js @@ -1,93 +1,93 @@ -/* 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/. */ - -var {WebChannel} = Cu.import("resource://gre/modules/WebChannel.jsm", {}); - -const TEST_URL_TAIL = "example.com/browser/browser/base/content/test/general/test_remoteTroubleshoot.html" -const TEST_URI_GOOD = Services.io.newURI("https://" + TEST_URL_TAIL, null, null); -const TEST_URI_BAD = Services.io.newURI("http://" + TEST_URL_TAIL, null, null); -const TEST_URI_GOOD_OBJECT = Services.io.newURI("https://" + TEST_URL_TAIL + "?object", null, null); - -// Creates a one-shot web-channel for the test data to be sent back from the test page. -function promiseChannelResponse(channelID, originOrPermission) { - return new Promise((resolve, reject) => { - let channel = new WebChannel(channelID, originOrPermission); - channel.listen((id, data, target) => { - channel.stopListening(); - resolve(data); - }); - }); -}; - -// Loads the specified URI in a new tab and waits for it to send us data on our -// test web-channel and resolves with that data. -function promiseNewChannelResponse(uri) { - let channelPromise = promiseChannelResponse("test-remote-troubleshooting-backchannel", - uri); - let tab = gBrowser.loadOneTab(uri.spec, { inBackground: false }); - return promiseTabLoaded(tab).then( - () => channelPromise - ).then(data => { - gBrowser.removeTab(tab); - return data; - }); -} - -add_task(function*() { - // We haven't set a permission yet - so even the "good" URI should fail. - let got = yield promiseNewChannelResponse(TEST_URI_GOOD); - // Should have no data. - Assert.ok(got.message === undefined, "should have failed to get any data"); - - // Add a permission manager entry for our URI. - Services.perms.add(TEST_URI_GOOD, - "remote-troubleshooting", - Services.perms.ALLOW_ACTION); - registerCleanupFunction(() => { - Services.perms.remove(TEST_URI_GOOD, "remote-troubleshooting"); - }); - - // Try again - now we are expecting a response with the actual data. - got = yield promiseNewChannelResponse(TEST_URI_GOOD); - - // Check some keys we expect to always get. - Assert.ok(got.message.extensions, "should have extensions"); - Assert.ok(got.message.graphics, "should have graphics"); - - // Check we have channel and build ID info: - Assert.equal(got.message.application.buildID, Services.appinfo.appBuildID, - "should have correct build ID"); - - let updateChannel = null; - try { - updateChannel = Cu.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel; - } catch (ex) {} - if (!updateChannel) { - Assert.ok(!('updateChannel' in got.message.application), - "should not have update channel where not available."); - } else { - Assert.equal(got.message.application.updateChannel, updateChannel, - "should have correct update channel."); - } - - - // And check some keys we know we decline to return. - Assert.ok(!got.message.modifiedPreferences, "should not have a modifiedPreferences key"); - Assert.ok(!got.message.crashes, "should not have crash info"); - - // Now a http:// URI - should get nothing even with the permission setup. - got = yield promiseNewChannelResponse(TEST_URI_BAD); - Assert.ok(got.message === undefined, "should have failed to get any data"); - - // Check that the page can send an object as well if it's in the whitelist - let webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist"; - let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref); - let newWhitelist = origWhitelist + " https://example.com"; - Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist); - registerCleanupFunction(() => { - Services.prefs.clearUserPref(webchannelWhitelistPref); - }); - got = yield promiseNewChannelResponse(TEST_URI_GOOD_OBJECT); - Assert.ok(got.message, "should have gotten some data back"); -}); +/* 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/. */ + +var {WebChannel} = Cu.import("resource://gre/modules/WebChannel.jsm", {}); + +const TEST_URL_TAIL = "example.com/browser/browser/base/content/test/general/test_remoteTroubleshoot.html" +const TEST_URI_GOOD = Services.io.newURI("https://" + TEST_URL_TAIL, null, null); +const TEST_URI_BAD = Services.io.newURI("http://" + TEST_URL_TAIL, null, null); +const TEST_URI_GOOD_OBJECT = Services.io.newURI("https://" + TEST_URL_TAIL + "?object", null, null); + +// Creates a one-shot web-channel for the test data to be sent back from the test page. +function promiseChannelResponse(channelID, originOrPermission) { + return new Promise((resolve, reject) => { + let channel = new WebChannel(channelID, originOrPermission); + channel.listen((id, data, target) => { + channel.stopListening(); + resolve(data); + }); + }); +}; + +// Loads the specified URI in a new tab and waits for it to send us data on our +// test web-channel and resolves with that data. +function promiseNewChannelResponse(uri) { + let channelPromise = promiseChannelResponse("test-remote-troubleshooting-backchannel", + uri); + let tab = gBrowser.loadOneTab(uri.spec, { inBackground: false }); + return promiseTabLoaded(tab).then( + () => channelPromise + ).then(data => { + gBrowser.removeTab(tab); + return data; + }); +} + +add_task(function*() { + // We haven't set a permission yet - so even the "good" URI should fail. + let got = yield promiseNewChannelResponse(TEST_URI_GOOD); + // Should have no data. + Assert.ok(got.message === undefined, "should have failed to get any data"); + + // Add a permission manager entry for our URI. + Services.perms.add(TEST_URI_GOOD, + "remote-troubleshooting", + Services.perms.ALLOW_ACTION); + registerCleanupFunction(() => { + Services.perms.remove(TEST_URI_GOOD, "remote-troubleshooting"); + }); + + // Try again - now we are expecting a response with the actual data. + got = yield promiseNewChannelResponse(TEST_URI_GOOD); + + // Check some keys we expect to always get. + Assert.ok(got.message.extensions, "should have extensions"); + Assert.ok(got.message.graphics, "should have graphics"); + + // Check we have channel and build ID info: + Assert.equal(got.message.application.buildID, Services.appinfo.appBuildID, + "should have correct build ID"); + + let updateChannel = null; + try { + updateChannel = Cu.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel; + } catch (ex) {} + if (!updateChannel) { + Assert.ok(!('updateChannel' in got.message.application), + "should not have update channel where not available."); + } else { + Assert.equal(got.message.application.updateChannel, updateChannel, + "should have correct update channel."); + } + + + // And check some keys we know we decline to return. + Assert.ok(!got.message.modifiedPreferences, "should not have a modifiedPreferences key"); + Assert.ok(!got.message.crashes, "should not have crash info"); + + // Now a http:// URI - should get nothing even with the permission setup. + got = yield promiseNewChannelResponse(TEST_URI_BAD); + Assert.ok(got.message === undefined, "should have failed to get any data"); + + // Check that the page can send an object as well if it's in the whitelist + let webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist"; + let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref); + let newWhitelist = origWhitelist + " https://example.com"; + Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist); + registerCleanupFunction(() => { + Services.prefs.clearUserPref(webchannelWhitelistPref); + }); + got = yield promiseNewChannelResponse(TEST_URI_GOOD_OBJECT); + Assert.ok(got.message, "should have gotten some data back"); +});
--- a/browser/base/content/test/general/browser_tabs_close_beforeunload.js +++ b/browser/base/content/test/general/browser_tabs_close_beforeunload.js @@ -1,49 +1,49 @@ -"use strict"; - -SimpleTest.requestCompleteLog(); - -SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]}); - -const FIRST_TAB = getRootDirectory(gTestPath) + "close_beforeunload_opens_second_tab.html"; -const SECOND_TAB = getRootDirectory(gTestPath) + "close_beforeunload.html"; - -add_task(function*() { - info("Opening first tab"); - let firstTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, FIRST_TAB); - let secondTabLoadedPromise; - let secondTab; - let tabOpened = new Promise(resolve => { - info("Adding tabopen listener"); - gBrowser.tabContainer.addEventListener("TabOpen", function tabOpenListener(e) { - info("Got tabopen, removing listener and waiting for load"); - gBrowser.tabContainer.removeEventListener("TabOpen", tabOpenListener, false, false); - secondTab = e.target; - secondTabLoadedPromise = BrowserTestUtils.browserLoaded(secondTab.linkedBrowser, false, SECOND_TAB); - resolve(); - }, false, false); - }); - info("Opening second tab using a click"); - yield ContentTask.spawn(firstTab.linkedBrowser, "", function*() { - content.document.getElementsByTagName("a")[0].click(); - }); - info("Waiting for the second tab to be opened"); - yield tabOpened; - info("Waiting for the load in that tab to finish"); - yield secondTabLoadedPromise; - - let closeBtn = document.getAnonymousElementByAttribute(secondTab, "anonid", "close-button"); - let closePromise = BrowserTestUtils.removeTab(secondTab, {dontRemove: true}); - info("closing second tab (which will self-close in beforeunload)"); - closeBtn.click(); - ok(secondTab.closing, "Second tab should be marked as closing synchronously."); - yield closePromise; - ok(secondTab.closing, "Second tab should still be marked as closing"); - ok(!secondTab.linkedBrowser, "Second tab's browser should be dead"); - ok(!firstTab.closing, "First tab should not be closing"); - ok(firstTab.linkedBrowser, "First tab's browser should be alive"); - info("closing first tab"); - yield BrowserTestUtils.removeTab(firstTab); - - ok(firstTab.closing, "First tab should be marked as closing"); - ok(!firstTab.linkedBrowser, "First tab's browser should be dead"); -}); +"use strict"; + +SimpleTest.requestCompleteLog(); + +SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]}); + +const FIRST_TAB = getRootDirectory(gTestPath) + "close_beforeunload_opens_second_tab.html"; +const SECOND_TAB = getRootDirectory(gTestPath) + "close_beforeunload.html"; + +add_task(function*() { + info("Opening first tab"); + let firstTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, FIRST_TAB); + let secondTabLoadedPromise; + let secondTab; + let tabOpened = new Promise(resolve => { + info("Adding tabopen listener"); + gBrowser.tabContainer.addEventListener("TabOpen", function tabOpenListener(e) { + info("Got tabopen, removing listener and waiting for load"); + gBrowser.tabContainer.removeEventListener("TabOpen", tabOpenListener, false, false); + secondTab = e.target; + secondTabLoadedPromise = BrowserTestUtils.browserLoaded(secondTab.linkedBrowser, false, SECOND_TAB); + resolve(); + }, false, false); + }); + info("Opening second tab using a click"); + yield ContentTask.spawn(firstTab.linkedBrowser, "", function*() { + content.document.getElementsByTagName("a")[0].click(); + }); + info("Waiting for the second tab to be opened"); + yield tabOpened; + info("Waiting for the load in that tab to finish"); + yield secondTabLoadedPromise; + + let closeBtn = document.getAnonymousElementByAttribute(secondTab, "anonid", "close-button"); + let closePromise = BrowserTestUtils.removeTab(secondTab, {dontRemove: true}); + info("closing second tab (which will self-close in beforeunload)"); + closeBtn.click(); + ok(secondTab.closing, "Second tab should be marked as closing synchronously."); + yield closePromise; + ok(secondTab.closing, "Second tab should still be marked as closing"); + ok(!secondTab.linkedBrowser, "Second tab's browser should be dead"); + ok(!firstTab.closing, "First tab should not be closing"); + ok(firstTab.linkedBrowser, "First tab's browser should be alive"); + info("closing first tab"); + yield BrowserTestUtils.removeTab(firstTab); + + ok(firstTab.closing, "First tab should be marked as closing"); + ok(!firstTab.linkedBrowser, "First tab's browser should be dead"); +});
--- a/browser/base/content/test/social/social_crash_content_helper.js +++ b/browser/base/content/test/social/social_crash_content_helper.js @@ -1,31 +1,31 @@ -/* Any copyright is dedicated to the Public Domain. -* http://creativecommons.org/publicdomain/zero/1.0/ */ - -var Cu = Components.utils; - -// Ideally we would use CrashTestUtils.jsm, but that's only available for -// xpcshell tests - so we just copy a ctypes crasher from it. -Cu.import("resource://gre/modules/ctypes.jsm"); -var crash = function() { // this will crash when called. - let zero = new ctypes.intptr_t(8); - let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t)); - badptr.contents -}; - - -var TestHelper = { - init: function() { - addMessageListener("social-test:crash", this); - }, - - receiveMessage: function(msg) { - switch (msg.name) { - case "social-test:crash": - privateNoteIntentionalCrash(); - crash(); - break; - } - }, -} - -TestHelper.init(); +/* Any copyright is dedicated to the Public Domain. +* http://creativecommons.org/publicdomain/zero/1.0/ */ + +var Cu = Components.utils; + +// Ideally we would use CrashTestUtils.jsm, but that's only available for +// xpcshell tests - so we just copy a ctypes crasher from it. +Cu.import("resource://gre/modules/ctypes.jsm"); +var crash = function() { // this will crash when called. + let zero = new ctypes.intptr_t(8); + let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t)); + badptr.contents +}; + + +var TestHelper = { + init: function() { + addMessageListener("social-test:crash", this); + }, + + receiveMessage: function(msg) { + switch (msg.name) { + case "social-test:crash": + privateNoteIntentionalCrash(); + crash(); + break; + } + }, +} + +TestHelper.init();
--- a/browser/base/content/test/urlbar/browser_bug1025195_switchToTabHavingURI_aOpenParams.js +++ b/browser/base/content/test/urlbar/browser_bug1025195_switchToTabHavingURI_aOpenParams.js @@ -1,94 +1,94 @@ -/* 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/. */ - -add_task(function test_ignoreFragment() { - let tabRefAboutHome = gBrowser.addTab("about:home#1"); - yield promiseTabLoaded(tabRefAboutHome); - let tabRefAboutMozilla = gBrowser.addTab("about:mozilla"); - yield promiseTabLoaded(tabRefAboutMozilla); - - gBrowser.selectedTab = tabRefAboutMozilla; - let numTabsAtStart = gBrowser.tabs.length; - - switchTab("about:home#1", true); - switchTab("about:mozilla", true); - - let hashChangePromise = new Promise(resolve => { - tabRefAboutHome.linkedBrowser.contentWindow.addEventListener("hashchange", resolve, false); - }); - switchTab("about:home#2", true, { ignoreFragment: true }); - is(tabRefAboutHome, gBrowser.selectedTab, "The same about:home tab should be switched to"); - yield hashChangePromise; - is(gBrowser.currentURI.ref, "2", "The ref should be updated to the new ref"); - switchTab("about:mozilla", true); - switchTab("about:home#1", false); - isnot(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should not be initial about:blank tab"); - is(gBrowser.tabs.length, numTabsAtStart + 1, "Should have one new tab opened"); - switchTab("about:mozilla", true); - switchTab("about:home", true, {ignoreFragment: true}); - yield promiseWaitForCondition(function() { - return tabRefAboutHome.linkedBrowser.currentURI.spec == "about:home"; - }); - is(tabRefAboutHome.linkedBrowser.currentURI.spec, "about:home", "about:home shouldn't have hash"); - switchTab("about:about", false, { ignoreFragment: true }); - cleanupTestTabs(); -}); - -add_task(function test_ignoreQueryString() { - let tabRefAboutHome = gBrowser.addTab("about:home?hello=firefox"); - yield promiseTabLoaded(tabRefAboutHome); - let tabRefAboutMozilla = gBrowser.addTab("about:mozilla"); - yield promiseTabLoaded(tabRefAboutMozilla); - gBrowser.selectedTab = tabRefAboutMozilla; - - switchTab("about:home?hello=firefox", true); - switchTab("about:home?hello=firefoxos", false); - // Remove the last opened tab to test ignoreQueryString option. - gBrowser.removeCurrentTab(); - switchTab("about:home?hello=firefoxos", true, { ignoreQueryString: true }); - is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab"); - is(gBrowser.currentURI.spec, "about:home?hello=firefox", "The spec should NOT be updated to the new query string"); - cleanupTestTabs(); -}); - -add_task(function test_replaceQueryString() { - let tabRefAboutHome = gBrowser.addTab("about:home?hello=firefox"); - yield promiseTabLoaded(tabRefAboutHome); - let tabRefAboutMozilla = gBrowser.addTab("about:mozilla"); - yield promiseTabLoaded(tabRefAboutMozilla); - gBrowser.selectedTab = tabRefAboutMozilla; - - switchTab("about:home", false); - switchTab("about:home?hello=firefox", true); - switchTab("about:home?hello=firefoxos", false); - // Remove the last opened tab to test replaceQueryString option. - gBrowser.removeCurrentTab(); - switchTab("about:home?hello=firefoxos", true, { replaceQueryString: true }); - is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab"); - // Wait for the tab to load the new URI spec. - yield promiseTabLoaded(tabRefAboutHome); - is(gBrowser.currentURI.spec, "about:home?hello=firefoxos", "The spec should be updated to the new spec"); - cleanupTestTabs(); -}); - -// Begin helpers - -function cleanupTestTabs() { - while (gBrowser.tabs.length > 1) - gBrowser.removeCurrentTab(); -} - -function switchTab(aURI, aShouldFindExistingTab, aOpenParams = {}) { - // Build the description before switchToTabHavingURI deletes the object properties. - let msg = "Should switch to existing " + aURI + " tab if one existed, " + - (aOpenParams.ignoreFragment ? "ignoring" : "including") + " fragment portion, " + - (aOpenParams.ignoreQueryString || aOpenParams.replaceQueryString ? - (aOpenParams.replaceQueryString ? "replacing" : "ignoring") : - "including" - ) + " query string."; - let tabFound = switchToTabHavingURI(aURI, true, aOpenParams); - is(tabFound, aShouldFindExistingTab, msg); -} - -registerCleanupFunction(cleanupTestTabs); +/* 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/. */ + +add_task(function test_ignoreFragment() { + let tabRefAboutHome = gBrowser.addTab("about:home#1"); + yield promiseTabLoaded(tabRefAboutHome); + let tabRefAboutMozilla = gBrowser.addTab("about:mozilla"); + yield promiseTabLoaded(tabRefAboutMozilla); + + gBrowser.selectedTab = tabRefAboutMozilla; + let numTabsAtStart = gBrowser.tabs.length; + + switchTab("about:home#1", true); + switchTab("about:mozilla", true); + + let hashChangePromise = new Promise(resolve => { + tabRefAboutHome.linkedBrowser.contentWindow.addEventListener("hashchange", resolve, false); + }); + switchTab("about:home#2", true, { ignoreFragment: true }); + is(tabRefAboutHome, gBrowser.selectedTab, "The same about:home tab should be switched to"); + yield hashChangePromise; + is(gBrowser.currentURI.ref, "2", "The ref should be updated to the new ref"); + switchTab("about:mozilla", true); + switchTab("about:home#1", false); + isnot(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should not be initial about:blank tab"); + is(gBrowser.tabs.length, numTabsAtStart + 1, "Should have one new tab opened"); + switchTab("about:mozilla", true); + switchTab("about:home", true, {ignoreFragment: true}); + yield promiseWaitForCondition(function() { + return tabRefAboutHome.linkedBrowser.currentURI.spec == "about:home"; + }); + is(tabRefAboutHome.linkedBrowser.currentURI.spec, "about:home", "about:home shouldn't have hash"); + switchTab("about:about", false, { ignoreFragment: true }); + cleanupTestTabs(); +}); + +add_task(function test_ignoreQueryString() { + let tabRefAboutHome = gBrowser.addTab("about:home?hello=firefox"); + yield promiseTabLoaded(tabRefAboutHome); + let tabRefAboutMozilla = gBrowser.addTab("about:mozilla"); + yield promiseTabLoaded(tabRefAboutMozilla); + gBrowser.selectedTab = tabRefAboutMozilla; + + switchTab("about:home?hello=firefox", true); + switchTab("about:home?hello=firefoxos", false); + // Remove the last opened tab to test ignoreQueryString option. + gBrowser.removeCurrentTab(); + switchTab("about:home?hello=firefoxos", true, { ignoreQueryString: true }); + is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab"); + is(gBrowser.currentURI.spec, "about:home?hello=firefox", "The spec should NOT be updated to the new query string"); + cleanupTestTabs(); +}); + +add_task(function test_replaceQueryString() { + let tabRefAboutHome = gBrowser.addTab("about:home?hello=firefox"); + yield promiseTabLoaded(tabRefAboutHome); + let tabRefAboutMozilla = gBrowser.addTab("about:mozilla"); + yield promiseTabLoaded(tabRefAboutMozilla); + gBrowser.selectedTab = tabRefAboutMozilla; + + switchTab("about:home", false); + switchTab("about:home?hello=firefox", true); + switchTab("about:home?hello=firefoxos", false); + // Remove the last opened tab to test replaceQueryString option. + gBrowser.removeCurrentTab(); + switchTab("about:home?hello=firefoxos", true, { replaceQueryString: true }); + is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab"); + // Wait for the tab to load the new URI spec. + yield promiseTabLoaded(tabRefAboutHome); + is(gBrowser.currentURI.spec, "about:home?hello=firefoxos", "The spec should be updated to the new spec"); + cleanupTestTabs(); +}); + +// Begin helpers + +function cleanupTestTabs() { + while (gBrowser.tabs.length > 1) + gBrowser.removeCurrentTab(); +} + +function switchTab(aURI, aShouldFindExistingTab, aOpenParams = {}) { + // Build the description before switchToTabHavingURI deletes the object properties. + let msg = "Should switch to existing " + aURI + " tab if one existed, " + + (aOpenParams.ignoreFragment ? "ignoring" : "including") + " fragment portion, " + + (aOpenParams.ignoreQueryString || aOpenParams.replaceQueryString ? + (aOpenParams.replaceQueryString ? "replacing" : "ignoring") : + "including" + ) + " query string."; + let tabFound = switchToTabHavingURI(aURI, true, aOpenParams); + is(tabFound, aShouldFindExistingTab, msg); +} + +registerCleanupFunction(cleanupTestTabs);
--- a/browser/components/preferences/in-content/tests/browser_bug1018066_resetScrollPosition.js +++ b/browser/components/preferences/in-content/tests/browser_bug1018066_resetScrollPosition.js @@ -1,24 +1,24 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -var originalWindowHeight; -registerCleanupFunction(function() { - window.resizeTo(window.outerWidth, originalWindowHeight); - while (gBrowser.tabs[1]) - gBrowser.removeTab(gBrowser.tabs[1]); -}); - -add_task(function() { - originalWindowHeight = window.outerHeight; - window.resizeTo(window.outerWidth, 300); - let prefs = yield openPreferencesViaOpenPreferencesAPI("paneApplications", undefined, {leaveOpen: true}); - is(prefs.selectedPane, "paneApplications", "Applications pane was selected"); - let mainContent = gBrowser.contentDocument.querySelector(".main-content"); - mainContent.scrollTop = 50; - is(mainContent.scrollTop, 50, "main-content should be scrolled 50 pixels"); - - gBrowser.contentWindow.gotoPref("paneGeneral"); - is(mainContent.scrollTop, 0, - "Switching to a different category should reset the scroll position"); -}); - +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +var originalWindowHeight; +registerCleanupFunction(function() { + window.resizeTo(window.outerWidth, originalWindowHeight); + while (gBrowser.tabs[1]) + gBrowser.removeTab(gBrowser.tabs[1]); +}); + +add_task(function() { + originalWindowHeight = window.outerHeight; + window.resizeTo(window.outerWidth, 300); + let prefs = yield openPreferencesViaOpenPreferencesAPI("paneApplications", undefined, {leaveOpen: true}); + is(prefs.selectedPane, "paneApplications", "Applications pane was selected"); + let mainContent = gBrowser.contentDocument.querySelector(".main-content"); + mainContent.scrollTop = 50; + is(mainContent.scrollTop, 50, "main-content should be scrolled 50 pixels"); + + gBrowser.contentWindow.gotoPref("paneGeneral"); + is(mainContent.scrollTop, 0, + "Switching to a different category should reset the scroll position"); +}); +
--- a/browser/components/preferences/in-content/tests/browser_defaultbrowser_alwayscheck.js +++ b/browser/components/preferences/in-content/tests/browser_defaultbrowser_alwayscheck.js @@ -1,103 +1,103 @@ -"use strict"; - -const CHECK_DEFAULT_INITIAL = Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"); - -add_task(function* clicking_make_default_checks_alwaysCheck_checkbox() { - yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences"); - - yield test_with_mock_shellservice({isDefault: false}, function*() { - let setDefaultPane = content.document.getElementById("setDefaultPane"); - Assert.equal(setDefaultPane.selectedIndex, "0", - "The 'make default' pane should be visible when not default"); - let alwaysCheck = content.document.getElementById("alwaysCheckDefault"); - Assert.ok(!alwaysCheck.checked, "Always Check is unchecked by default"); - Assert.ok(!Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), - "alwaysCheck pref should be false by default in test runs"); - - let setDefaultButton = content.document.getElementById("setDefaultButton"); - setDefaultButton.click(); - content.window.gMainPane.updateSetDefaultBrowser(); - - yield ContentTaskUtils.waitForCondition(() => alwaysCheck.checked, - "'Always Check' checkbox should get checked after clicking the 'Set Default' button"); - - Assert.ok(alwaysCheck.checked, - "Clicking 'Make Default' checks the 'Always Check' checkbox"); - Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), - "Checking the checkbox should set the pref to true"); - Assert.ok(alwaysCheck.disabled, - "'Always Check' checkbox is locked with default browser and alwaysCheck=true"); - Assert.equal(setDefaultPane.selectedIndex, "1", - "The 'make default' pane should not be visible when default"); - Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), - "checkDefaultBrowser pref is now enabled"); - }); - - gBrowser.removeCurrentTab(); - Services.prefs.clearUserPref("browser.shell.checkDefaultBrowser"); -}); - -add_task(function* clicking_make_default_checks_alwaysCheck_checkbox() { - Services.prefs.lockPref("browser.shell.checkDefaultBrowser"); - yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences"); - - yield test_with_mock_shellservice({isDefault: false}, function*() { - let setDefaultPane = content.document.getElementById("setDefaultPane"); - Assert.equal(setDefaultPane.selectedIndex, "0", - "The 'make default' pane should be visible when not default"); - let alwaysCheck = content.document.getElementById("alwaysCheckDefault"); - Assert.ok(alwaysCheck.disabled, "Always Check is disabled when locked"); - Assert.ok(alwaysCheck.checked, - "Always Check is checked because defaultPref is true and pref is locked"); - Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), - "alwaysCheck pref should ship with 'true' by default"); - - let setDefaultButton = content.document.getElementById("setDefaultButton"); - setDefaultButton.click(); - content.window.gMainPane.updateSetDefaultBrowser(); - - yield ContentTaskUtils.waitForCondition(() => setDefaultPane.selectedIndex == "1", - "Browser is now default"); - - Assert.ok(alwaysCheck.checked, - "'Always Check' is still checked because it's locked"); - Assert.ok(alwaysCheck.disabled, - "'Always Check is disabled because it's locked"); - Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), - "The pref is locked and so doesn't get changed"); - }); - - Services.prefs.unlockPref("browser.shell.checkDefaultBrowser"); - gBrowser.removeCurrentTab(); -}); - -registerCleanupFunction(function() { - Services.prefs.unlockPref("browser.shell.checkDefaultBrowser"); - Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", CHECK_DEFAULT_INITIAL); -}); - -function* test_with_mock_shellservice(options, testFn) { - yield ContentTask.spawn(gBrowser.selectedBrowser, options, function*(options) { - let doc = content.document; - let win = doc.defaultView; - win.oldShellService = win.getShellService(); - let mockShellService = { - _isDefault: false, - isDefaultBrowser() { - return this._isDefault; - }, - setDefaultBrowser() { - this._isDefault = true; - }, - }; - win.getShellService = function() { - return mockShellService; - } - mockShellService._isDefault = options.isDefault; - win.gMainPane.updateSetDefaultBrowser(); - }); - - yield ContentTask.spawn(gBrowser.selectedBrowser, null, testFn); - - Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", CHECK_DEFAULT_INITIAL); -} +"use strict"; + +const CHECK_DEFAULT_INITIAL = Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"); + +add_task(function* clicking_make_default_checks_alwaysCheck_checkbox() { + yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences"); + + yield test_with_mock_shellservice({isDefault: false}, function*() { + let setDefaultPane = content.document.getElementById("setDefaultPane"); + Assert.equal(setDefaultPane.selectedIndex, "0", + "The 'make default' pane should be visible when not default"); + let alwaysCheck = content.document.getElementById("alwaysCheckDefault"); + Assert.ok(!alwaysCheck.checked, "Always Check is unchecked by default"); + Assert.ok(!Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), + "alwaysCheck pref should be false by default in test runs"); + + let setDefaultButton = content.document.getElementById("setDefaultButton"); + setDefaultButton.click(); + content.window.gMainPane.updateSetDefaultBrowser(); + + yield ContentTaskUtils.waitForCondition(() => alwaysCheck.checked, + "'Always Check' checkbox should get checked after clicking the 'Set Default' button"); + + Assert.ok(alwaysCheck.checked, + "Clicking 'Make Default' checks the 'Always Check' checkbox"); + Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), + "Checking the checkbox should set the pref to true"); + Assert.ok(alwaysCheck.disabled, + "'Always Check' checkbox is locked with default browser and alwaysCheck=true"); + Assert.equal(setDefaultPane.selectedIndex, "1", + "The 'make default' pane should not be visible when default"); + Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), + "checkDefaultBrowser pref is now enabled"); + }); + + gBrowser.removeCurrentTab(); + Services.prefs.clearUserPref("browser.shell.checkDefaultBrowser"); +}); + +add_task(function* clicking_make_default_checks_alwaysCheck_checkbox() { + Services.prefs.lockPref("browser.shell.checkDefaultBrowser"); + yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences"); + + yield test_with_mock_shellservice({isDefault: false}, function*() { + let setDefaultPane = content.document.getElementById("setDefaultPane"); + Assert.equal(setDefaultPane.selectedIndex, "0", + "The 'make default' pane should be visible when not default"); + let alwaysCheck = content.document.getElementById("alwaysCheckDefault"); + Assert.ok(alwaysCheck.disabled, "Always Check is disabled when locked"); + Assert.ok(alwaysCheck.checked, + "Always Check is checked because defaultPref is true and pref is locked"); + Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), + "alwaysCheck pref should ship with 'true' by default"); + + let setDefaultButton = content.document.getElementById("setDefaultButton"); + setDefaultButton.click(); + content.window.gMainPane.updateSetDefaultBrowser(); + + yield ContentTaskUtils.waitForCondition(() => setDefaultPane.selectedIndex == "1", + "Browser is now default"); + + Assert.ok(alwaysCheck.checked, + "'Always Check' is still checked because it's locked"); + Assert.ok(alwaysCheck.disabled, + "'Always Check is disabled because it's locked"); + Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), + "The pref is locked and so doesn't get changed"); + }); + + Services.prefs.unlockPref("browser.shell.checkDefaultBrowser"); + gBrowser.removeCurrentTab(); +}); + +registerCleanupFunction(function() { + Services.prefs.unlockPref("browser.shell.checkDefaultBrowser"); + Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", CHECK_DEFAULT_INITIAL); +}); + +function* test_with_mock_shellservice(options, testFn) { + yield ContentTask.spawn(gBrowser.selectedBrowser, options, function*(options) { + let doc = content.document; + let win = doc.defaultView; + win.oldShellService = win.getShellService(); + let mockShellService = { + _isDefault: false, + isDefaultBrowser() { + return this._isDefault; + }, + setDefaultBrowser() { + this._isDefault = true; + }, + }; + win.getShellService = function() { + return mockShellService; + } + mockShellService._isDefault = options.isDefault; + win.gMainPane.updateSetDefaultBrowser(); + }); + + yield ContentTask.spawn(gBrowser.selectedBrowser, null, testFn); + + Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", CHECK_DEFAULT_INITIAL); +}
--- a/browser/components/preferences/in-content/tests/browser_homepages_filter_aboutpreferences.js +++ b/browser/components/preferences/in-content/tests/browser_homepages_filter_aboutpreferences.js @@ -1,20 +1,20 @@ -add_task(function() { - is(gBrowser.currentURI.spec, "about:blank", "Test starts with about:blank open"); - yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home"); - yield openPreferencesViaOpenPreferencesAPI("paneGeneral", null, {leaveOpen: true}); - let doc = gBrowser.contentDocument; - is(gBrowser.currentURI.spec, "about:preferences#general", - "#general should be in the URI for about:preferences"); - let oldHomepagePref = Services.prefs.getCharPref("browser.startup.homepage"); - - let useCurrent = doc.getElementById("useCurrent"); - useCurrent.click(); - - is(gBrowser.tabs.length, 3, "Three tabs should be open"); - is(Services.prefs.getCharPref("browser.startup.homepage"), "about:blank|about:home", - "about:blank and about:home should be the only homepages set"); - - Services.prefs.setCharPref("browser.startup.homepage", oldHomepagePref); - yield BrowserTestUtils.removeTab(gBrowser.selectedTab); - yield BrowserTestUtils.removeTab(gBrowser.selectedTab); -}); +add_task(function() { + is(gBrowser.currentURI.spec, "about:blank", "Test starts with about:blank open"); + yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home"); + yield openPreferencesViaOpenPreferencesAPI("paneGeneral", null, {leaveOpen: true}); + let doc = gBrowser.contentDocument; + is(gBrowser.currentURI.spec, "about:preferences#general", + "#general should be in the URI for about:preferences"); + let oldHomepagePref = Services.prefs.getCharPref("browser.startup.homepage"); + + let useCurrent = doc.getElementById("useCurrent"); + useCurrent.click(); + + is(gBrowser.tabs.length, 3, "Three tabs should be open"); + is(Services.prefs.getCharPref("browser.startup.homepage"), "about:blank|about:home", + "about:blank and about:home should be the only homepages set"); + + Services.prefs.setCharPref("browser.startup.homepage", oldHomepagePref); + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); +});
--- a/browser/components/preferences/in-content/tests/browser_notifications_do_not_disturb.js +++ b/browser/components/preferences/in-content/tests/browser_notifications_do_not_disturb.js @@ -1,44 +1,44 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - - -registerCleanupFunction(function() { - while (gBrowser.tabs[1]) - gBrowser.removeTab(gBrowser.tabs[1]); -}); - -add_task(function() { - let prefs = yield openPreferencesViaOpenPreferencesAPI("paneContent", undefined, {leaveOpen: true}); - is(prefs.selectedPane, "paneContent", "Content pane was selected"); - - let doc = gBrowser.contentDocument; - let notificationsDoNotDisturbRow = doc.getElementById("notificationsDoNotDisturbRow"); - if (notificationsDoNotDisturbRow.hidden) { - todo(false, "Do not disturb is not available on this platform"); - return; - } - - let alertService; - try { - alertService = Cc["@mozilla.org/alerts-service;1"] - .getService(Ci.nsIAlertsService) - .QueryInterface(Ci.nsIAlertsDoNotDisturb); - } catch (ex) { - ok(true, "Do not disturb is not available on this platform: " + ex.message); - return; - } - - let checkbox = doc.getElementById("notificationsDoNotDisturb"); - ok(!checkbox.checked, "Checkbox should not be checked by default"); - ok(!alertService.manualDoNotDisturb, "Do not disturb should be off by default"); - - let checkboxChanged = waitForEvent(checkbox, "command") - checkbox.click(); - yield checkboxChanged; - ok(alertService.manualDoNotDisturb, "Do not disturb should be enabled when checked"); - - checkboxChanged = waitForEvent(checkbox, "command") - checkbox.click(); - yield checkboxChanged; - ok(!alertService.manualDoNotDisturb, "Do not disturb should be disabled when unchecked"); -}); +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + + +registerCleanupFunction(function() { + while (gBrowser.tabs[1]) + gBrowser.removeTab(gBrowser.tabs[1]); +}); + +add_task(function() { + let prefs = yield openPreferencesViaOpenPreferencesAPI("paneContent", undefined, {leaveOpen: true}); + is(prefs.selectedPane, "paneContent", "Content pane was selected"); + + let doc = gBrowser.contentDocument; + let notificationsDoNotDisturbRow = doc.getElementById("notificationsDoNotDisturbRow"); + if (notificationsDoNotDisturbRow.hidden) { + todo(false, "Do not disturb is not available on this platform"); + return; + } + + let alertService; + try { + alertService = Cc["@mozilla.org/alerts-service;1"] + .getService(Ci.nsIAlertsService) + .QueryInterface(Ci.nsIAlertsDoNotDisturb); + } catch (ex) { + ok(true, "Do not disturb is not available on this platform: " + ex.message); + return; + } + + let checkbox = doc.getElementById("notificationsDoNotDisturb"); + ok(!checkbox.checked, "Checkbox should not be checked by default"); + ok(!alertService.manualDoNotDisturb, "Do not disturb should be off by default"); + + let checkboxChanged = waitForEvent(checkbox, "command") + checkbox.click(); + yield checkboxChanged; + ok(alertService.manualDoNotDisturb, "Do not disturb should be enabled when checked"); + + checkboxChanged = waitForEvent(checkbox, "command") + checkbox.click(); + yield checkboxChanged; + ok(!alertService.manualDoNotDisturb, "Do not disturb should be disabled when unchecked"); +});
--- a/browser/components/preferences/in-content/tests/browser_permissions_urlFieldHidden.js +++ b/browser/components/preferences/in-content/tests/browser_permissions_urlFieldHidden.js @@ -1,45 +1,45 @@ -"use strict"; - -const PERMISSIONS_URL = "chrome://browser/content/preferences/permissions.xul"; - -add_task(function* urlFieldVisibleForPopupPermissions(finish) { - yield openPreferencesViaOpenPreferencesAPI("paneContent", null, {leaveOpen: true}); - let win = gBrowser.selectedBrowser.contentWindow; - let doc = win.document; - let popupPolicyCheckbox = doc.getElementById("popupPolicy"); - ok(!popupPolicyCheckbox.checked, "popupPolicyCheckbox should be unchecked by default"); - popupPolicyCheckbox.click(); - let popupPolicyButton = doc.getElementById("popupPolicyButton"); - ok(popupPolicyButton, "popupPolicyButton found"); - let dialogPromise = promiseLoadSubDialog(PERMISSIONS_URL); - popupPolicyButton.click(); - let dialog = yield dialogPromise; - ok(dialog, "dialog loaded"); - - let urlLabel = dialog.document.getElementById("urlLabel"); - ok(!urlLabel.hidden, "urlLabel should be visible when one of block/session/allow visible"); - let url = dialog.document.getElementById("url"); - ok(!url.hidden, "url should be visible when one of block/session/allow visible"); - - popupPolicyCheckbox.click(); - gBrowser.removeCurrentTab(); -}); - -add_task(function* urlFieldHiddenForNotificationPermissions() { - yield openPreferencesViaOpenPreferencesAPI("paneContent", null, {leaveOpen: true}); - let win = gBrowser.selectedBrowser.contentWindow; - let doc = win.document; - let notificationsPolicyButton = doc.getElementById("notificationsPolicyButton"); - ok(notificationsPolicyButton, "notificationsPolicyButton found"); - let dialogPromise = promiseLoadSubDialog(PERMISSIONS_URL); - notificationsPolicyButton.click(); - let dialog = yield dialogPromise; - ok(dialog, "dialog loaded"); - - let urlLabel = dialog.document.getElementById("urlLabel"); - ok(urlLabel.hidden, "urlLabel should be hidden as requested"); - let url = dialog.document.getElementById("url"); - ok(url.hidden, "url should be hidden as requested"); - - gBrowser.removeCurrentTab(); -}); +"use strict"; + +const PERMISSIONS_URL = "chrome://browser/content/preferences/permissions.xul"; + +add_task(function* urlFieldVisibleForPopupPermissions(finish) { + yield openPreferencesViaOpenPreferencesAPI("paneContent", null, {leaveOpen: true}); + let win = gBrowser.selectedBrowser.contentWindow; + let doc = win.document; + let popupPolicyCheckbox = doc.getElementById("popupPolicy"); + ok(!popupPolicyCheckbox.checked, "popupPolicyCheckbox should be unchecked by default"); + popupPolicyCheckbox.click(); + let popupPolicyButton = doc.getElementById("popupPolicyButton"); + ok(popupPolicyButton, "popupPolicyButton found"); + let dialogPromise = promiseLoadSubDialog(PERMISSIONS_URL); + popupPolicyButton.click(); + let dialog = yield dialogPromise; + ok(dialog, "dialog loaded"); + + let urlLabel = dialog.document.getElementById("urlLabel"); + ok(!urlLabel.hidden, "urlLabel should be visible when one of block/session/allow visible"); + let url = dialog.document.getElementById("url"); + ok(!url.hidden, "url should be visible when one of block/session/allow visible"); + + popupPolicyCheckbox.click(); + gBrowser.removeCurrentTab(); +}); + +add_task(function* urlFieldHiddenForNotificationPermissions() { + yield openPreferencesViaOpenPreferencesAPI("paneContent", null, {leaveOpen: true}); + let win = gBrowser.selectedBrowser.contentWindow; + let doc = win.document; + let notificationsPolicyButton = doc.getElementById("notificationsPolicyButton"); + ok(notificationsPolicyButton, "notificationsPolicyButton found"); + let dialogPromise = promiseLoadSubDialog(PERMISSIONS_URL); + notificationsPolicyButton.click(); + let dialog = yield dialogPromise; + ok(dialog, "dialog loaded"); + + let urlLabel = dialog.document.getElementById("urlLabel"); + ok(urlLabel.hidden, "urlLabel should be hidden as requested"); + let url = dialog.document.getElementById("url"); + ok(url.hidden, "url should be hidden as requested"); + + gBrowser.removeCurrentTab(); +});
--- a/browser/components/preferences/in-content/tests/browser_sanitizeOnShutdown_prefLocked.js +++ b/browser/components/preferences/in-content/tests/browser_sanitizeOnShutdown_prefLocked.js @@ -1,37 +1,37 @@ -"use strict"; - -function switchToCustomHistoryMode(doc) { - // Select the last item in the menulist. - let menulist = doc.getElementById("historyMode"); - menulist.focus(); - EventUtils.sendKey("UP"); -} - -function testPrefStateMatchesLockedState() { - let win = gBrowser.contentWindow; - let doc = win.document; - switchToCustomHistoryMode(doc); - - let checkbox = doc.getElementById("alwaysClear"); - let preference = doc.getElementById("privacy.sanitize.sanitizeOnShutdown"); - is(checkbox.disabled, preference.locked, "Always Clear checkbox should be enabled when preference is not locked."); - - gBrowser.removeCurrentTab(); -} - -add_task(function setup() { - registerCleanupFunction(function resetPreferences() { - Services.prefs.unlockPref("privacy.sanitize.sanitizeOnShutdown"); - }); -}); - -add_task(function test_preference_enabled_when_unlocked() { - yield openPreferencesViaOpenPreferencesAPI("panePrivacy", undefined, {leaveOpen: true}); - testPrefStateMatchesLockedState(); -}); - -add_task(function test_preference_disabled_when_locked() { - Services.prefs.lockPref("privacy.sanitize.sanitizeOnShutdown"); - yield openPreferencesViaOpenPreferencesAPI("panePrivacy", undefined, {leaveOpen: true}); - testPrefStateMatchesLockedState(); -}); +"use strict"; + +function switchToCustomHistoryMode(doc) { + // Select the last item in the menulist. + let menulist = doc.getElementById("historyMode"); + menulist.focus(); + EventUtils.sendKey("UP"); +} + +function testPrefStateMatchesLockedState() { + let win = gBrowser.contentWindow; + let doc = win.document; + switchToCustomHistoryMode(doc); + + let checkbox = doc.getElementById("alwaysClear"); + let preference = doc.getElementById("privacy.sanitize.sanitizeOnShutdown"); + is(checkbox.disabled, preference.locked, "Always Clear checkbox should be enabled when preference is not locked."); + + gBrowser.removeCurrentTab(); +} + +add_task(function setup() { + registerCleanupFunction(function resetPreferences() { + Services.prefs.unlockPref("privacy.sanitize.sanitizeOnShutdown"); + }); +}); + +add_task(function test_preference_enabled_when_unlocked() { + yield openPreferencesViaOpenPreferencesAPI("panePrivacy", undefined, {leaveOpen: true}); + testPrefStateMatchesLockedState(); +}); + +add_task(function test_preference_disabled_when_locked() { + Services.prefs.lockPref("privacy.sanitize.sanitizeOnShutdown"); + yield openPreferencesViaOpenPreferencesAPI("panePrivacy", undefined, {leaveOpen: true}); + testPrefStateMatchesLockedState(); +});
--- a/browser/components/shell/ShellService.jsm +++ b/browser/components/shell/ShellService.jsm @@ -1,107 +1,107 @@ -/* 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/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["ShellService"]; - -const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; - -Cu.import("resource://gre/modules/AppConstants.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry", - "resource://gre/modules/WindowsRegistry.jsm"); - -/** - * Internal functionality to save and restore the docShell.allow* properties. - */ -let ShellServiceInternal = { - /** - * Used to determine whether or not to offer "Set as desktop background" - * functionality. Even if shell service is available it is not - * guaranteed that it is able to set the background for every desktop - * which is especially true for Linux with its many different desktop - * environments. - */ - get canSetDesktopBackground() { - if (AppConstants.platform == "win" || - AppConstants.platform == "macosx") { - return true; - } - - if (AppConstants.platform == "linux") { - if (this.shellService) { - let linuxShellService = this.shellService - .QueryInterface(Ci.nsIGNOMEShellService); - return linuxShellService.canSetDesktopBackground; - } - } - - return false; - }, - - /** - * Used to determine whether or not to show a "Set Default Browser" - * query dialog. This attribute is true if the application is starting - * up and "browser.shell.checkDefaultBrowser" is true, otherwise it - * is false. - */ - _checkedThisSession: false, - get shouldCheckDefaultBrowser() { - // If we've already checked, the browser has been started and this is a - // new window open, and we don't want to check again. - if (this._checkedThisSession) { - return false; - } - - if (!Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser")) { - return false; - } - - if (AppConstants.platform == "win") { - let optOutValue = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, - "Software\\Mozilla\\Firefox", - "DefaultBrowserOptOut"); - WindowsRegistry.removeRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, - "Software\\Mozilla\\Firefox", - "DefaultBrowserOptOut"); - if (optOutValue == "True") { - Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", false); - return false; - } - } - - return true; - }, - - set shouldCheckDefaultBrowser(shouldCheck) { - Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", !!shouldCheck); - }, - - isDefaultBrowser(startupCheck, forAllTypes) { - // If this is the first browser window, maintain internal state that we've - // checked this session (so that subsequent window opens don't show the - // default browser dialog). - if (startupCheck) { - this._checkedThisSession = true; - } - if (this.shellService) { - return this.shellService.isDefaultBrowser(startupCheck, forAllTypes); - } - } -}; - -XPCOMUtils.defineLazyServiceGetter(ShellServiceInternal, "shellService", - "@mozilla.org/browser/shell-service;1", Ci.nsIShellService); - -/** - * The external API exported by this module. - */ -this.ShellService = new Proxy(ShellServiceInternal, { - get(target, name) { - return name in target ? target[name] : - target.shellService[name]; - } -}); +/* 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/. */ + +"use strict"; + +this.EXPORTED_SYMBOLS = ["ShellService"]; + +const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; + +Cu.import("resource://gre/modules/AppConstants.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry", + "resource://gre/modules/WindowsRegistry.jsm"); + +/** + * Internal functionality to save and restore the docShell.allow* properties. + */ +let ShellServiceInternal = { + /** + * Used to determine whether or not to offer "Set as desktop background" + * functionality. Even if shell service is available it is not + * guaranteed that it is able to set the background for every desktop + * which is especially true for Linux with its many different desktop + * environments. + */ + get canSetDesktopBackground() { + if (AppConstants.platform == "win" || + AppConstants.platform == "macosx") { + return true; + } + + if (AppConstants.platform == "linux") { + if (this.shellService) { + let linuxShellService = this.shellService + .QueryInterface(Ci.nsIGNOMEShellService); + return linuxShellService.canSetDesktopBackground; + } + } + + return false; + }, + + /** + * Used to determine whether or not to show a "Set Default Browser" + * query dialog. This attribute is true if the application is starting + * up and "browser.shell.checkDefaultBrowser" is true, otherwise it + * is false. + */ + _checkedThisSession: false, + get shouldCheckDefaultBrowser() { + // If we've already checked, the browser has been started and this is a + // new window open, and we don't want to check again. + if (this._checkedThisSession) { + return false; + } + + if (!Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser")) { + return false; + } + + if (AppConstants.platform == "win") { + let optOutValue = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, + "Software\\Mozilla\\Firefox", + "DefaultBrowserOptOut"); + WindowsRegistry.removeRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, + "Software\\Mozilla\\Firefox", + "DefaultBrowserOptOut"); + if (optOutValue == "True") { + Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", false); + return false; + } + } + + return true; + }, + + set shouldCheckDefaultBrowser(shouldCheck) { + Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", !!shouldCheck); + }, + + isDefaultBrowser(startupCheck, forAllTypes) { + // If this is the first browser window, maintain internal state that we've + // checked this session (so that subsequent window opens don't show the + // default browser dialog). + if (startupCheck) { + this._checkedThisSession = true; + } + if (this.shellService) { + return this.shellService.isDefaultBrowser(startupCheck, forAllTypes); + } + } +}; + +XPCOMUtils.defineLazyServiceGetter(ShellServiceInternal, "shellService", + "@mozilla.org/browser/shell-service;1", Ci.nsIShellService); + +/** + * The external API exported by this module. + */ +this.ShellService = new Proxy(ShellServiceInternal, { + get(target, name) { + return name in target ? target[name] : + target.shellService[name]; + } +});
--- a/browser/extensions/pocket/content/panels/js/vendor/jquery.tokeninput.min.js +++ b/browser/extensions/pocket/content/panels/js/vendor/jquery.tokeninput.min.js @@ -1,944 +1,944 @@ -/* - * jQuery Plugin: Tokenizing Autocomplete Text Entry - * Version 1.6.0 - * - * Copyright (c) 2009 James Smith (http://loopj.com) - * Licensed jointly under the GPL and MIT licenses, - * choose which one suits your project best! - * - * Licensed under MIT - * With modifications - * - */ - -(function ($) { -// Default settings -var DEFAULT_SETTINGS = { - // Search settings - method: "GET", - contentType: "json", - queryParam: "q", - searchDelay: 300, - minChars: 1, - propertyToSearch: "name", - jsonContainer: null, - scrollKeyboard: false, - - // Display settings - hintText: null, - noResultsText: null, - noResultsHideDropdown: false, - searchingText: null, - deleteText: "×", - animateDropdown: true, - emptyInputLength: null, - - // Tokenization settings - tokenLimit: null, - tokenDelimiter: ",", - preventDuplicates: false, - - // Output settings - tokenValue: "id", - - // Prepopulation settings - prePopulate: null, - processPrePopulate: false, - - // Manipulation settings - idPrefix: "token-input-", - - // Formatters - resultsFormatter: function(item){ return "<li>" + item[this.propertyToSearch]+ "</li>" }, - tokenFormatter: function(item) { return "<li><p>" + item[this.propertyToSearch] + "</p></li>" }, - - // Validations - validateItem: null, - - // Force selections only on mouse click - noHoverSelect: false, - - // Callbacks - onResult: null, - onAdd: null, - onDelete: null, - onReady: null -}; - -// Default classes to use when theming -var DEFAULT_CLASSES = { - tokenList: "token-input-list", - token: "token-input-token", - tokenDelete: "token-input-delete-token", - selectedToken: "token-input-selected-token", - highlightedToken: "token-input-highlighted-token", - dropdown: "token-input-dropdown", - dropdownItem: "token-input-dropdown-item", - dropdownItem2: "token-input-dropdown-item2", - selectedDropdownItem: "token-input-selected-dropdown-item", - inputToken: "token-input-input-token" -}; - -// Input box position "enum" -var POSITION = { - BEFORE: 0, - AFTER: 1, - END: 2 -}; - -// Keys "enum" -var KEY = { - BACKSPACE: 8, - TAB: 9, - ENTER: 13, - ESCAPE: 27, - SPACE: 32, - PAGE_UP: 33, - PAGE_DOWN: 34, - END: 35, - HOME: 36, - LEFT: 37, - UP: 38, - RIGHT: 39, - DOWN: 40, - NUMPAD_ENTER: 108, - COMMA: 188 -}; - -// Additional public (exposed) methods -var methods = { - init: function(url_or_data_or_function, options) { - var settings = $.extend({}, DEFAULT_SETTINGS, options || {}); - - return this.each(function () { - $(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings)); - }); - }, - clear: function() { - this.data("tokenInputObject").clear(); - return this; - }, - add: function(item) { - this.data("tokenInputObject").add(item); - return this; - }, - remove: function(item) { - this.data("tokenInputObject").remove(item); - return this; - }, - get: function() { - return this.data("tokenInputObject").getTokens(); - } -} - -// Expose the .tokenInput function to jQuery as a plugin -$.fn.tokenInput = function (method) { - // Method calling and initialization logic - if(methods[method]) { - return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); - } else { - return methods.init.apply(this, arguments); - } -}; - -// TokenList class for each input -$.TokenList = function (input, url_or_data, settings) { - // - // Initialization - // - - // Configure the data source - if($.type(url_or_data) === "string" || $.type(url_or_data) === "function") { - // Set the url to query against - settings.url = url_or_data; - - // If the URL is a function, evaluate it here to do our initalization work - var url = computeURL(); - - // Make a smart guess about cross-domain if it wasn't explicitly specified - if(settings.crossDomain === undefined) { - if(url.indexOf("://") === -1) { - settings.crossDomain = false; - } else { - settings.crossDomain = (location.href.split(/\/+/g)[1] !== url.split(/\/+/g)[1]); - } - } - } else if(typeof(url_or_data) === "object") { - // Set the local data to search through - settings.local_data = url_or_data; - } - - // Build class names - if(settings.classes) { - // Use custom class names - settings.classes = $.extend({}, DEFAULT_CLASSES, settings.classes); - } else if(settings.theme) { - // Use theme-suffixed default class names - settings.classes = {}; - $.each(DEFAULT_CLASSES, function(key, value) { - settings.classes[key] = value + "-" + settings.theme; - }); - } else { - settings.classes = DEFAULT_CLASSES; - } - - - // Save the tokens - var saved_tokens = []; - - // Keep track of the number of tokens in the list - var token_count = 0; - - // Basic cache to save on db hits - var cache = new $.TokenList.Cache(); - - // Keep track of the timeout, old vals - var timeout; - var input_val; - - function tokenize(){ - var item = $(selected_dropdown_item).data("tokeninput"); - if(!item && settings.textToData){ - item = settings.textToData(input_box.val()); - } - - if(item) { - add_token(item); - hidden_input.change(); - return false; - } - } - - // Create a new text input an attach keyup events - var input_box = $("<input type=\"text\" autocomplete=\"off\">") - .css({ - outline: "none" - }) - .attr("id", settings.idPrefix + input.id) - .focus(function () { - if (settings.minChars == 0) { - setTimeout(function(){do_search();}, 5); - } - if (settings.tokenLimit === null || settings.tokenLimit !== token_count) { - show_dropdown_hint(); - } - }) - .blur(function () { - tokenize(); - hide_dropdown(); - $(this).val(""); - }) - .bind("keyup keydown blur update", resize_input) - .keydown(function (event) { - var previous_token; - var next_token; - - switch(event.keyCode) { - case KEY.LEFT: - case KEY.RIGHT: - case KEY.UP: - case KEY.DOWN: - if(!$(this).val()) { - previous_token = input_token.prev(); - next_token = input_token.next(); - - if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) { - // Check if there is a previous/next token and it is selected - if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) { - deselect_token($(selected_token), POSITION.BEFORE); - } else { - deselect_token($(selected_token), POSITION.AFTER); - } - } else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) { - // We are moving left, select the previous token if it exists - select_token($(previous_token.get(0))); - } else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) { - // We are moving right, select the next token if it exists - select_token($(next_token.get(0))); - } - } else { - if (event.keyCode === KEY.UP || event.keyCode === KEY.DOWN) { - var dropdown_item = null; - if(!selected_dropdown_item && (event.keyCode === KEY.DOWN)) { - dropdown_item = $('.token-input-dropdown li').first(); - } - else if(event.keyCode === KEY.DOWN) { - dropdown_item = $(selected_dropdown_item).next(); - } else { - dropdown_item = $(selected_dropdown_item).prev(); - } - - if(dropdown_item.length) { - select_dropdown_item(dropdown_item,true); - } - else if (!(event.keyCode === KEY.DOWN) && $(selected_dropdown_item).length) { - deselect_dropdown_item($(selected_dropdown_item)); - } - return false; - } - } - break; - - case KEY.BACKSPACE: - previous_token = input_token.prev(); - - if(!$(this).val().length) { - if(selected_token) { - delete_token($(selected_token)); - hidden_input.change(); - } else if(previous_token.length) { - select_token($(previous_token.get(0))); - } - - return false; - } else if($(this).val().length === 1) { - hide_dropdown(); - } else { - // set a timeout just long enough to let this function finish. - setTimeout(function(){do_search();}, 5); - } - break; - - case KEY.TAB: - case KEY.ENTER: - case KEY.NUMPAD_ENTER: - case KEY.COMMA: - if (event.keyCode != KEY.ENTER && event.keyCode != KEY.NUMPAD_ENTER) - { - event.preventDefault(); - } - tokenize(); - break; - - case KEY.ESCAPE: - hide_dropdown(); - return true; - - default: - if(String.fromCharCode(event.which)) { - // set a timeout just long enough to let this function finish. - setTimeout(function(){do_search();}, 5); - } - break; - } - }); - - // Keep a reference to the original input box - var hidden_input = $(input) - .hide() - .val("") - .focus(function () { - input_box.focus(); - }) - .blur(function () { - input_box.blur(); - }); - - // Keep a reference to the selected token and dropdown item - var selected_token = null; - var selected_token_index = 0; - var selected_dropdown_item = null; - - // The list to store the token items in - var token_list = $("<ul />") - .addClass(settings.classes.tokenList) - .click(function (event) { - var li = $(event.target).closest("li"); - if(li && li.get(0) && $.data(li.get(0), "tokeninput")) { - toggle_select_token(li); - } else { - // Deselect selected token - if(selected_token) { - deselect_token($(selected_token), POSITION.END); - } - - // Focus input box - input_box.focus(); - } - }) - .mouseover(function (event) { - var li = $(event.target).closest("li"); - if(li && selected_token !== this) { - li.addClass(settings.classes.highlightedToken); - } - }) - .mouseout(function (event) { - var li = $(event.target).closest("li"); - if(li && selected_token !== this) { - li.removeClass(settings.classes.highlightedToken); - } - }) - .insertBefore(hidden_input); - - // The token holding the input box - var input_token = $("<li />") - .addClass(settings.classes.inputToken) - .appendTo(token_list) - .append(input_box); - - // The list to store the dropdown items in - var dropdown = $("<div>") - .addClass(settings.classes.dropdown) - .appendTo("body") - .hide(); - - // Magic element to help us resize the text input - var input_resizer = $("<tester/>") - .insertAfter(input_box) - .css({ - position: "absolute", - top: -9999, - left: -9999, - width: "auto", - fontSize: input_box.css("fontSize"), - fontFamily: input_box.css("fontFamily"), - fontWeight: input_box.css("fontWeight"), - letterSpacing: input_box.css("letterSpacing"), - whiteSpace: "nowrap" - }); - - // Pre-populate list if items exist - hidden_input.val(""); - var li_data = settings.prePopulate || hidden_input.data("pre"); - if(settings.processPrePopulate && $.isFunction(settings.onResult)) { - li_data = settings.onResult.call(hidden_input, li_data); - } - if(li_data && li_data.length) { - $.each(li_data, function (index, value) { - insert_token(value); - checkTokenLimit(); - }); - } - - // Initialization is done - if($.isFunction(settings.onReady)) { - settings.onReady.call(); - if (settings.minChars == 0) - { - setTimeout(function(){do_search();}, 5); - } - } - - // - // Public functions - // - - this.clear = function() { - token_list.children("li").each(function() { - if ($(this).children("input").length === 0) { - delete_token($(this)); - } - }); - } - - this.add = function(item) { - add_token(item); - } - - this.remove = function(item) { - token_list.children("li").each(function() { - if ($(this).children("input").length === 0) { - var currToken = $(this).data("tokeninput"); - var match = true; - for (var prop in item) { - if (item[prop] !== currToken[prop]) { - match = false; - break; - } - } - if (match) { - delete_token($(this)); - } - } - }); - } - - this.getTokens = function() { - return saved_tokens; - } - - // - // Private functions - // - - function checkTokenLimit() { - if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { - input_box.hide(); - hide_dropdown(); - return; - } - } - - function resize_input() { - if(input_val === (input_val = input_box.val())) {return;} - - // Enter new content into resizer and resize input accordingly - var escaped = input_val.replace(/&/g, '&').replace(/\s/g,' ').replace(/</g, '<').replace(/>/g, '>'); - input_resizer.html(escaped); - var minwidth = 30; - if (settings.emptyInputLength && token_list.children().length < 2) { - minwidth = settings.emptyInputLength; - } - input_box.width(input_resizer.width() + minwidth); - } - - function is_printable_character(keycode) { - return ((keycode >= 48 && keycode <= 90) || // 0-1a-z - (keycode >= 96 && keycode <= 111) || // numpad 0-9 + - / * . - (keycode >= 186 && keycode <= 192) || // ; = , - . / ^ - (keycode >= 219 && keycode <= 222)); // ( \ ) ' - } - - // Inner function to a token to the list - function insert_token(item) { - var this_token = settings.tokenFormatter(item); - this_token = $(this_token) - .addClass(settings.classes.token) - .insertBefore(input_token); - - // The 'delete token' button - $("<span>" + settings.deleteText + "</span>") - .addClass(settings.classes.tokenDelete) - .appendTo(this_token) - .click(function () { - delete_token($(this).parent()); - hidden_input.change(); - return false; - }); - - // Store data on the token - var token_data = {"id": item.id}; - token_data[settings.propertyToSearch] = item[settings.propertyToSearch]; - token_data.item = item; - $.data(this_token.get(0), "tokeninput", item); - - // Save this token for duplicate checking - saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index)); - selected_token_index++; - - // Update the hidden input - update_hidden_input(saved_tokens, hidden_input); - - token_count += 1; - - // Check the token limit - if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { - input_box.hide(); - hide_dropdown(); - } - - return this_token; - } - - // Add a token to the token list based on user input - function add_token (item) { - if(!item) return; - - // Check for item validation - if ($.isFunction(settings.validateItem) && !settings.validateItem(item)) { - return false; - } - - var callback = settings.onAdd; - - // See if the token already exists and select it if we don't want duplicates - if(token_count > 0 && settings.preventDuplicates) { - var found_existing_token = null; - token_list.children().each(function () { - var existing_token = $(this); - var existing_data = $.data(existing_token.get(0), "tokeninput"); - if(existing_data && existing_data.id === item.id) { - found_existing_token = existing_token; - return false; - } - }); - - if(found_existing_token) { - select_token(found_existing_token); - input_token.insertAfter(found_existing_token); - input_box.focus(); - return; - } - } - - // Insert the new tokens - if(settings.tokenLimit == null || token_count < settings.tokenLimit) { - insert_token(item); - checkTokenLimit(); - } - - // Clear input box - input_box.val(""); - - // Don't show the help dropdown, they've got the idea - hide_dropdown(); - - // Execute the onAdd callback if defined - if($.isFunction(callback)) { - callback.call(hidden_input,item); - } - } - - // Select a token in the token list - function select_token (token) { - token.addClass(settings.classes.selectedToken); - selected_token = token.get(0); - - // Hide input box - input_box.val(""); - - // Hide dropdown if it is visible (eg if we clicked to select token) - hide_dropdown(); - } - - // Deselect a token in the token list - function deselect_token (token, position) { - token.removeClass(settings.classes.selectedToken); - selected_token = null; - - if(position === POSITION.BEFORE) { - input_token.insertBefore(token); - selected_token_index--; - } else if(position === POSITION.AFTER) { - input_token.insertAfter(token); - selected_token_index++; - } else { - input_token.appendTo(token_list); - selected_token_index = token_count; - } - - // Show the input box and give it focus again - input_box.focus(); - } - - // Toggle selection of a token in the token list - function toggle_select_token(token) { - var previous_selected_token = selected_token; - - if(selected_token) { - deselect_token($(selected_token), POSITION.END); - } - - if(previous_selected_token === token.get(0)) { - deselect_token(token, POSITION.END); - } else { - select_token(token); - } - } - - // Delete a token from the token list - function delete_token (token) { - // Remove the id from the saved list - var token_data = $.data(token.get(0), "tokeninput"); - var callback = settings.onDelete; - - var index = token.prevAll().length; - if(index > selected_token_index) index--; - - // Delete the token - token.remove(); - selected_token = null; - - // Show the input box and give it focus again - input_box.focus(); - - // Remove this token from the saved list - saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1)); - if(index < selected_token_index) selected_token_index--; - - // Update the hidden input - update_hidden_input(saved_tokens, hidden_input); - - token_count -= 1; - - if(settings.tokenLimit !== null) { - input_box - .show() - .val("") - .focus(); - } - - // Execute the onDelete callback if defined - if($.isFunction(callback)) { - callback.call(hidden_input,token_data); - } - } - - // Update the hidden input box value - function update_hidden_input(saved_tokens, hidden_input) { - var token_values = $.map(saved_tokens, function (el) { - return el[settings.tokenValue]; - }); - hidden_input.val(token_values.join(settings.tokenDelimiter)); - - } - - // Hide and clear the results dropdown - function hide_dropdown () { - dropdown.hide().empty(); - selected_dropdown_item = null; - if (settings.onHideDropdown) - settings.onHideDropdown(); - } - - function show_dropdown() { - dropdown - .css({ - position: "absolute", - top: $(token_list).offset().top + $(token_list).outerHeight(), - left: $(token_list).offset().left, - zindex: 999 - }) - .show(); - if (settings.onShowDropdown) - settings.onShowDropdown(); - } - - function show_dropdown_searching () { - if(settings.searchingText) { - dropdown.html("<p>"+settings.searchingText+"</p>"); - show_dropdown(); - } - } - - function show_dropdown_hint () { - if(settings.hintText) { - dropdown.html("<p>"+settings.hintText+"</p>"); - show_dropdown(); - } - } - - // Highlight the query part of the search term - function highlight_term(value, term) { - return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>"); - } - - function find_value_and_highlight_term(template, value, term) { - return template.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + value + ")(?![^<>]*>)(?![^&;]+;)", "g"), highlight_term(value, term)); - } - - // Populate the results dropdown with some results - function populate_dropdown (query, results) { - if(results && results.length) { - dropdown.empty(); - var dropdown_ul = $("<ul>") - .appendTo(dropdown) - .mouseover(function (event) { - select_dropdown_item($(event.target).closest("li")); - }) - .mousedown(function (event) { - add_token($(event.target).closest("li").data("tokeninput")); - hidden_input.change(); - return false; - }) - .hide(); - if (settings.noHoverSelect) { - dropdown_ul.off('mouseover'); - dropdown_ul.on('mouseover',function (event) { - $(this).find("li").removeClass(settings.classes.selectedDropdownItem); - $(event.target).closest("li").addClass(settings.classes.selectedDropdownItem); - }); - } - - $.each(results, function(index, value) { - var this_li = settings.resultsFormatter(value); - - // this_li = find_value_and_highlight_term(this_li ,value[settings.propertyToSearch], query); - - this_li = $(this_li).appendTo(dropdown_ul); - - if(index % 2) { - this_li.addClass(settings.classes.dropdownItem); - } else { - this_li.addClass(settings.classes.dropdownItem2); - } - - // if(index === 0) { - // select_dropdown_item(this_li); - // } - - $.data(this_li.get(0), "tokeninput", value); - }); - - show_dropdown(); - - if(settings.animateDropdown) { - dropdown_ul.slideDown("fast"); - } else { - dropdown_ul.show(); - } - } else { - if(settings.noResultsText) { - dropdown.html("<p>"+settings.noResultsText+"</p>"); - show_dropdown(); - } - if (settings.noResultsHideDropdown) { - hide_dropdown(); - } - } - } - - // Highlight an item in the results dropdown - function select_dropdown_item (item,withkeyboard) { - if(item) { - if(selected_dropdown_item) { - deselect_dropdown_item($(selected_dropdown_item)); - } - if (settings.scrollKeyboard && withkeyboard) { - var list = $('.token-input-dropdown-tag ul'); - var listheight = list.height(); - var itemheight = item.outerHeight(); - var itemtop = item.position().top; - if (itemtop > listheight) { - var listscroll = list.scrollTop(); - list.scrollTop(listscroll + itemheight); - } - else if (itemtop < 0) { - var listscroll = list.scrollTop(); - list.scrollTop(listscroll - itemheight); - } - - } - item.addClass(settings.classes.selectedDropdownItem); - selected_dropdown_item = item.get(0); - } - } - - // Remove highlighting from an item in the results dropdown - function deselect_dropdown_item (item) { - item.removeClass(settings.classes.selectedDropdownItem); - selected_dropdown_item = null; - } - - // Do a search and show the "searching" dropdown if the input is longer - // than settings.minChars - function do_search() { - var query = input_box.val().toLowerCase(); - if(query && query.length || settings.minChars == 0) { - if(selected_token) { - deselect_token($(selected_token), POSITION.AFTER); - } - - if(query.length >= settings.minChars) { - show_dropdown_searching(); - clearTimeout(timeout); - - timeout = setTimeout(function(){ - run_search(query); - }, settings.searchDelay); - } else { - hide_dropdown(); - } - } - } - - // Do the actual search - function run_search(query) { - var cache_key = query + computeURL(); - var cached_results = cache.get(cache_key); - if(cached_results) { - populate_dropdown(query, cached_results); - } else { - // Are we doing an ajax search or local data search? - if(settings.url) { - var url = computeURL(); - // Extract exisiting get params - var ajax_params = {}; - ajax_params.data = {}; - if(url.indexOf("?") > -1) { - var parts = url.split("?"); - ajax_params.url = parts[0]; - - var param_array = parts[1].split("&"); - $.each(param_array, function (index, value) { - var kv = value.split("="); - ajax_params.data[kv[0]] = kv[1]; - }); - } else { - ajax_params.url = url; - } - - // Prepare the request - ajax_params.data[settings.queryParam] = query; - ajax_params.type = settings.method; - ajax_params.dataType = settings.contentType; - if(settings.crossDomain) { - ajax_params.dataType = "jsonp"; - } - - // Attach the success callback - ajax_params.success = function(results) { - if($.isFunction(settings.onResult)) { - results = settings.onResult.call(hidden_input, results); - } - cache.add(cache_key, settings.jsonContainer ? results[settings.jsonContainer] : results); - - // only populate the dropdown if the results are associated with the active search query - if(input_box.val().toLowerCase() === query) { - populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results); - } - }; - - // Make the request - $.ajax(ajax_params); - } else if(settings.search_function){ - settings.search_function(query, function(results){ - cache.add(cache_key, results); - populate_dropdown(query, results); - }); - } else if(settings.local_data) { - // Do the search through local data - var results = $.grep(settings.local_data, function (row) { - return row[settings.propertyToSearch].toLowerCase().indexOf(query.toLowerCase()) > -1; - }); - - if($.isFunction(settings.onResult)) { - results = settings.onResult.call(hidden_input, results); - } - cache.add(cache_key, results); - populate_dropdown(query, results); - } - } - } - - // compute the dynamic URL - function computeURL() { - var url = settings.url; - if(typeof settings.url == 'function') { - url = settings.url.call(); - } - return url; - } -}; - -// Really basic cache for the results -$.TokenList.Cache = function (options) { - var settings = $.extend({ - max_size: 500 - }, options); - - var data = {}; - var size = 0; - - var flush = function () { - data = {}; - size = 0; - }; - - this.add = function (query, results) { - if(size > settings.max_size) { - flush(); - } - - if(!data[query]) { - size += 1; - } - - data[query] = results; - }; - - this.get = function (query) { - return data[query]; - }; -}; -}(jQuery)); +/* + * jQuery Plugin: Tokenizing Autocomplete Text Entry + * Version 1.6.0 + * + * Copyright (c) 2009 James Smith (http://loopj.com) + * Licensed jointly under the GPL and MIT licenses, + * choose which one suits your project best! + * + * Licensed under MIT + * With modifications + * + */ + +(function ($) { +// Default settings +var DEFAULT_SETTINGS = { + // Search settings + method: "GET", + contentType: "json", + queryParam: "q", + searchDelay: 300, + minChars: 1, + propertyToSearch: "name", + jsonContainer: null, + scrollKeyboard: false, + + // Display settings + hintText: null, + noResultsText: null, + noResultsHideDropdown: false, + searchingText: null, + deleteText: "×", + animateDropdown: true, + emptyInputLength: null, + + // Tokenization settings + tokenLimit: null, + tokenDelimiter: ",", + preventDuplicates: false, + + // Output settings + tokenValue: "id", + + // Prepopulation settings + prePopulate: null, + processPrePopulate: false, + + // Manipulation settings + idPrefix: "token-input-", + + // Formatters + resultsFormatter: function(item){ return "<li>" + item[this.propertyToSearch]+ "</li>" }, + tokenFormatter: function(item) { return "<li><p>" + item[this.propertyToSearch] + "</p></li>" }, + + // Validations + validateItem: null, + + // Force selections only on mouse click + noHoverSelect: false, + + // Callbacks + onResult: null, + onAdd: null, + onDelete: null, + onReady: null +}; + +// Default classes to use when theming +var DEFAULT_CLASSES = { + tokenList: "token-input-list", + token: "token-input-token", + tokenDelete: "token-input-delete-token", + selectedToken: "token-input-selected-token", + highlightedToken: "token-input-highlighted-token", + dropdown: "token-input-dropdown", + dropdownItem: "token-input-dropdown-item", + dropdownItem2: "token-input-dropdown-item2", + selectedDropdownItem: "token-input-selected-dropdown-item", + inputToken: "token-input-input-token" +}; + +// Input box position "enum" +var POSITION = { + BEFORE: 0, + AFTER: 1, + END: 2 +}; + +// Keys "enum" +var KEY = { + BACKSPACE: 8, + TAB: 9, + ENTER: 13, + ESCAPE: 27, + SPACE: 32, + PAGE_UP: 33, + PAGE_DOWN: 34, + END: 35, + HOME: 36, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + NUMPAD_ENTER: 108, + COMMA: 188 +}; + +// Additional public (exposed) methods +var methods = { + init: function(url_or_data_or_function, options) { + var settings = $.extend({}, DEFAULT_SETTINGS, options || {}); + + return this.each(function () { + $(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings)); + }); + }, + clear: function() { + this.data("tokenInputObject").clear(); + return this; + }, + add: function(item) { + this.data("tokenInputObject").add(item); + return this; + }, + remove: function(item) { + this.data("tokenInputObject").remove(item); + return this; + }, + get: function() { + return this.data("tokenInputObject").getTokens(); + } +} + +// Expose the .tokenInput function to jQuery as a plugin +$.fn.tokenInput = function (method) { + // Method calling and initialization logic + if(methods[method]) { + return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); + } else { + return methods.init.apply(this, arguments); + } +}; + +// TokenList class for each input +$.TokenList = function (input, url_or_data, settings) { + // + // Initialization + // + + // Configure the data source + if($.type(url_or_data) === "string" || $.type(url_or_data) === "function") { + // Set the url to query against + settings.url = url_or_data; + + // If the URL is a function, evaluate it here to do our initalization work + var url = computeURL(); + + // Make a smart guess about cross-domain if it wasn't explicitly specified + if(settings.crossDomain === undefined) { + if(url.indexOf("://") === -1) { + settings.crossDomain = false; + } else { + settings.crossDomain = (location.href.split(/\/+/g)[1] !== url.split(/\/+/g)[1]); + } + } + } else if(typeof(url_or_data) === "object") { + // Set the local data to search through + settings.local_data = url_or_data; + } + + // Build class names + if(settings.classes) { + // Use custom class names + settings.classes = $.extend({}, DEFAULT_CLASSES, settings.classes); + } else if(settings.theme) { + // Use theme-suffixed default class names + settings.classes = {}; + $.each(DEFAULT_CLASSES, function(key, value) { + settings.classes[key] = value + "-" + settings.theme; + }); + } else { + settings.classes = DEFAULT_CLASSES; + } + + + // Save the tokens + var saved_tokens = []; + + // Keep track of the number of tokens in the list + var token_count = 0; + + // Basic cache to save on db hits + var cache = new $.TokenList.Cache(); + + // Keep track of the timeout, old vals + var timeout; + var input_val; + + function tokenize(){ + var item = $(selected_dropdown_item).data("tokeninput"); + if(!item && settings.textToData){ + item = settings.textToData(input_box.val()); + } + + if(item) { + add_token(item); + hidden_input.change(); + return false; + } + } + + // Create a new text input an attach keyup events + var input_box = $("<input type=\"text\" autocomplete=\"off\">") + .css({ + outline: "none" + }) + .attr("id", settings.idPrefix + input.id) + .focus(function () { + if (settings.minChars == 0) { + setTimeout(function(){do_search();}, 5); + } + if (settings.tokenLimit === null || settings.tokenLimit !== token_count) { + show_dropdown_hint(); + } + }) + .blur(function () { + tokenize(); + hide_dropdown(); + $(this).val(""); + }) + .bind("keyup keydown blur update", resize_input) + .keydown(function (event) { + var previous_token; + var next_token; + + switch(event.keyCode) { + case KEY.LEFT: + case KEY.RIGHT: + case KEY.UP: + case KEY.DOWN: + if(!$(this).val()) { + previous_token = input_token.prev(); + next_token = input_token.next(); + + if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) { + // Check if there is a previous/next token and it is selected + if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) { + deselect_token($(selected_token), POSITION.BEFORE); + } else { + deselect_token($(selected_token), POSITION.AFTER); + } + } else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) { + // We are moving left, select the previous token if it exists + select_token($(previous_token.get(0))); + } else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) { + // We are moving right, select the next token if it exists + select_token($(next_token.get(0))); + } + } else { + if (event.keyCode === KEY.UP || event.keyCode === KEY.DOWN) { + var dropdown_item = null; + if(!selected_dropdown_item && (event.keyCode === KEY.DOWN)) { + dropdown_item = $('.token-input-dropdown li').first(); + } + else if(event.keyCode === KEY.DOWN) { + dropdown_item = $(selected_dropdown_item).next(); + } else { + dropdown_item = $(selected_dropdown_item).prev(); + } + + if(dropdown_item.length) { + select_dropdown_item(dropdown_item,true); + } + else if (!(event.keyCode === KEY.DOWN) && $(selected_dropdown_item).length) { + deselect_dropdown_item($(selected_dropdown_item)); + } + return false; + } + } + break; + + case KEY.BACKSPACE: + previous_token = input_token.prev(); + + if(!$(this).val().length) { + if(selected_token) { + delete_token($(selected_token)); + hidden_input.change(); + } else if(previous_token.length) { + select_token($(previous_token.get(0))); + } + + return false; + } else if($(this).val().length === 1) { + hide_dropdown(); + } else { + // set a timeout just long enough to let this function finish. + setTimeout(function(){do_search();}, 5); + } + break; + + case KEY.TAB: + case KEY.ENTER: + case KEY.NUMPAD_ENTER: + case KEY.COMMA: + if (event.keyCode != KEY.ENTER && event.keyCode != KEY.NUMPAD_ENTER) + { + event.preventDefault(); + } + tokenize(); + break; + + case KEY.ESCAPE: + hide_dropdown(); + return true; + + default: + if(String.fromCharCode(event.which)) { + // set a timeout just long enough to let this function finish. + setTimeout(function(){do_search();}, 5); + } + break; + } + }); + + // Keep a reference to the original input box + var hidden_input = $(input) + .hide() + .val("") + .focus(function () { + input_box.focus(); + }) + .blur(function () { + input_box.blur(); + }); + + // Keep a reference to the selected token and dropdown item + var selected_token = null; + var selected_token_index = 0; + var selected_dropdown_item = null; + + // The list to store the token items in + var token_list = $("<ul />") + .addClass(settings.classes.tokenList) + .click(function (event) { + var li = $(event.target).closest("li"); + if(li && li.get(0) && $.data(li.get(0), "tokeninput")) { + toggle_select_token(li); + } else { + // Deselect selected token + if(selected_token) { + deselect_token($(selected_token), POSITION.END); + } + + // Focus input box + input_box.focus(); + } + }) + .mouseover(function (event) { + var li = $(event.target).closest("li"); + if(li && selected_token !== this) { + li.addClass(settings.classes.highlightedToken); + } + }) + .mouseout(function (event) { + var li = $(event.target).closest("li"); + if(li && selected_token !== this) { + li.removeClass(settings.classes.highlightedToken); + } + }) + .insertBefore(hidden_input); + + // The token holding the input box + var input_token = $("<li />") + .addClass(settings.classes.inputToken) + .appendTo(token_list) + .append(input_box); + + // The list to store the dropdown items in + var dropdown = $("<div>") + .addClass(settings.classes.dropdown) + .appendTo("body") + .hide(); + + // Magic element to help us resize the text input + var input_resizer = $("<tester/>") + .insertAfter(input_box) + .css({ + position: "absolute", + top: -9999, + left: -9999, + width: "auto", + fontSize: input_box.css("fontSize"), + fontFamily: input_box.css("fontFamily"), + fontWeight: input_box.css("fontWeight"), + letterSpacing: input_box.css("letterSpacing"), + whiteSpace: "nowrap" + }); + + // Pre-populate list if items exist + hidden_input.val(""); + var li_data = settings.prePopulate || hidden_input.data("pre"); + if(settings.processPrePopulate && $.isFunction(settings.onResult)) { + li_data = settings.onResult.call(hidden_input, li_data); + } + if(li_data && li_data.length) { + $.each(li_data, function (index, value) { + insert_token(value); + checkTokenLimit(); + }); + } + + // Initialization is done + if($.isFunction(settings.onReady)) { + settings.onReady.call(); + if (settings.minChars == 0) + { + setTimeout(function(){do_search();}, 5); + } + } + + // + // Public functions + // + + this.clear = function() { + token_list.children("li").each(function() { + if ($(this).children("input").length === 0) { + delete_token($(this)); + } + }); + } + + this.add = function(item) { + add_token(item); + } + + this.remove = function(item) { + token_list.children("li").each(function() { + if ($(this).children("input").length === 0) { + var currToken = $(this).data("tokeninput"); + var match = true; + for (var prop in item) { + if (item[prop] !== currToken[prop]) { + match = false; + break; + } + } + if (match) { + delete_token($(this)); + } + } + }); + } + + this.getTokens = function() { + return saved_tokens; + } + + // + // Private functions + // + + function checkTokenLimit() { + if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { + input_box.hide(); + hide_dropdown(); + return; + } + } + + function resize_input() { + if(input_val === (input_val = input_box.val())) {return;} + + // Enter new content into resizer and resize input accordingly + var escaped = input_val.replace(/&/g, '&').replace(/\s/g,' ').replace(/</g, '<').replace(/>/g, '>'); + input_resizer.html(escaped); + var minwidth = 30; + if (settings.emptyInputLength && token_list.children().length < 2) { + minwidth = settings.emptyInputLength; + } + input_box.width(input_resizer.width() + minwidth); + } + + function is_printable_character(keycode) { + return ((keycode >= 48 && keycode <= 90) || // 0-1a-z + (keycode >= 96 && keycode <= 111) || // numpad 0-9 + - / * . + (keycode >= 186 && keycode <= 192) || // ; = , - . / ^ + (keycode >= 219 && keycode <= 222)); // ( \ ) ' + } + + // Inner function to a token to the list + function insert_token(item) { + var this_token = settings.tokenFormatter(item); + this_token = $(this_token) + .addClass(settings.classes.token) + .insertBefore(input_token); + + // The 'delete token' button + $("<span>" + settings.deleteText + "</span>") + .addClass(settings.classes.tokenDelete) + .appendTo(this_token) + .click(function () { + delete_token($(this).parent()); + hidden_input.change(); + return false; + }); + + // Store data on the token + var token_data = {"id": item.id}; + token_data[settings.propertyToSearch] = item[settings.propertyToSearch]; + token_data.item = item; + $.data(this_token.get(0), "tokeninput", item); + + // Save this token for duplicate checking + saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index)); + selected_token_index++; + + // Update the hidden input + update_hidden_input(saved_tokens, hidden_input); + + token_count += 1; + + // Check the token limit + if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) { + input_box.hide(); + hide_dropdown(); + } + + return this_token; + } + + // Add a token to the token list based on user input + function add_token (item) { + if(!item) return; + + // Check for item validation + if ($.isFunction(settings.validateItem) && !settings.validateItem(item)) { + return false; + } + + var callback = settings.onAdd; + + // See if the token already exists and select it if we don't want duplicates + if(token_count > 0 && settings.preventDuplicates) { + var found_existing_token = null; + token_list.children().each(function () { + var existing_token = $(this); + var existing_data = $.data(existing_token.get(0), "tokeninput"); + if(existing_data && existing_data.id === item.id) { + found_existing_token = existing_token; + return false; + } + }); + + if(found_existing_token) { + select_token(found_existing_token); + input_token.insertAfter(found_existing_token); + input_box.focus(); + return; + } + } + + // Insert the new tokens + if(settings.tokenLimit == null || token_count < settings.tokenLimit) { + insert_token(item); + checkTokenLimit(); + } + + // Clear input box + input_box.val(""); + + // Don't show the help dropdown, they've got the idea + hide_dropdown(); + + // Execute the onAdd callback if defined + if($.isFunction(callback)) { + callback.call(hidden_input,item); + } + } + + // Select a token in the token list + function select_token (token) { + token.addClass(settings.classes.selectedToken); + selected_token = token.get(0); + + // Hide input box + input_box.val(""); + + // Hide dropdown if it is visible (eg if we clicked to select token) + hide_dropdown(); + } + + // Deselect a token in the token list + function deselect_token (token, position) { + token.removeClass(settings.classes.selectedToken); + selected_token = null; + + if(position === POSITION.BEFORE) { + input_token.insertBefore(token); + selected_token_index--; + } else if(position === POSITION.AFTER) { + input_token.insertAfter(token); + selected_token_index++; + } else { + input_token.appendTo(token_list); + selected_token_index = token_count; + } + + // Show the input box and give it focus again + input_box.focus(); + } + + // Toggle selection of a token in the token list + function toggle_select_token(token) { + var previous_selected_token = selected_token; + + if(selected_token) { + deselect_token($(selected_token), POSITION.END); + } + + if(previous_selected_token === token.get(0)) { + deselect_token(token, POSITION.END); + } else { + select_token(token); + } + } + + // Delete a token from the token list + function delete_token (token) { + // Remove the id from the saved list + var token_data = $.data(token.get(0), "tokeninput"); + var callback = settings.onDelete; + + var index = token.prevAll().length; + if(index > selected_token_index) index--; + + // Delete the token + token.remove(); + selected_token = null; + + // Show the input box and give it focus again + input_box.focus(); + + // Remove this token from the saved list + saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1)); + if(index < selected_token_index) selected_token_index--; + + // Update the hidden input + update_hidden_input(saved_tokens, hidden_input); + + token_count -= 1; + + if(settings.tokenLimit !== null) { + input_box + .show() + .val("") + .focus(); + } + + // Execute the onDelete callback if defined + if($.isFunction(callback)) { + callback.call(hidden_input,token_data); + } + } + + // Update the hidden input box value + function update_hidden_input(saved_tokens, hidden_input) { + var token_values = $.map(saved_tokens, function (el) { + return el[settings.tokenValue]; + }); + hidden_input.val(token_values.join(settings.tokenDelimiter)); + + } + + // Hide and clear the results dropdown + function hide_dropdown () { + dropdown.hide().empty(); + selected_dropdown_item = null; + if (settings.onHideDropdown) + settings.onHideDropdown(); + } + + function show_dropdown() { + dropdown + .css({ + position: "absolute", + top: $(token_list).offset().top + $(token_list).outerHeight(), + left: $(token_list).offset().left, + zindex: 999 + }) + .show(); + if (settings.onShowDropdown) + settings.onShowDropdown(); + } + + function show_dropdown_searching () { + if(settings.searchingText) { + dropdown.html("<p>"+settings.searchingText+"</p>"); + show_dropdown(); + } + } + + function show_dropdown_hint () { + if(settings.hintText) { + dropdown.html("<p>"+settings.hintText+"</p>"); + show_dropdown(); + } + } + + // Highlight the query part of the search term + function highlight_term(value, term) { + return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>"); + } + + function find_value_and_highlight_term(template, value, term) { + return template.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + value + ")(?![^<>]*>)(?![^&;]+;)", "g"), highlight_term(value, term)); + } + + // Populate the results dropdown with some results + function populate_dropdown (query, results) { + if(results && results.length) { + dropdown.empty(); + var dropdown_ul = $("<ul>") + .appendTo(dropdown) + .mouseover(function (event) { + select_dropdown_item($(event.target).closest("li")); + }) + .mousedown(function (event) { + add_token($(event.target).closest("li").data("tokeninput")); + hidden_input.change(); + return false; + }) + .hide(); + if (settings.noHoverSelect) { + dropdown_ul.off('mouseover'); + dropdown_ul.on('mouseover',function (event) { + $(this).find("li").removeClass(settings.classes.selectedDropdownItem); + $(event.target).closest("li").addClass(settings.classes.selectedDropdownItem); + }); + } + + $.each(results, function(index, value) { + var this_li = settings.resultsFormatter(value); + + // this_li = find_value_and_highlight_term(this_li ,value[settings.propertyToSearch], query); + + this_li = $(this_li).appendTo(dropdown_ul); + + if(index % 2) { + this_li.addClass(settings.classes.dropdownItem); + } else { + this_li.addClass(settings.classes.dropdownItem2); + } + + // if(index === 0) { + // select_dropdown_item(this_li); + // } + + $.data(this_li.get(0), "tokeninput", value); + }); + + show_dropdown(); + + if(settings.animateDropdown) { + dropdown_ul.slideDown("fast"); + } else { + dropdown_ul.show(); + } + } else { + if(settings.noResultsText) { + dropdown.html("<p>"+settings.noResultsText+"</p>"); + show_dropdown(); + } + if (settings.noResultsHideDropdown) { + hide_dropdown(); + } + } + } + + // Highlight an item in the results dropdown + function select_dropdown_item (item,withkeyboard) { + if(item) { + if(selected_dropdown_item) { + deselect_dropdown_item($(selected_dropdown_item)); + } + if (settings.scrollKeyboard && withkeyboard) { + var list = $('.token-input-dropdown-tag ul'); + var listheight = list.height(); + var itemheight = item.outerHeight(); + var itemtop = item.position().top; + if (itemtop > listheight) { + var listscroll = list.scrollTop(); + list.scrollTop(listscroll + itemheight); + } + else if (itemtop < 0) { + var listscroll = list.scrollTop(); + list.scrollTop(listscroll - itemheight); + } + + } + item.addClass(settings.classes.selectedDropdownItem); + selected_dropdown_item = item.get(0); + } + } + + // Remove highlighting from an item in the results dropdown + function deselect_dropdown_item (item) { + item.removeClass(settings.classes.selectedDropdownItem); + selected_dropdown_item = null; + } + + // Do a search and show the "searching" dropdown if the input is longer + // than settings.minChars + function do_search() { + var query = input_box.val().toLowerCase(); + if(query && query.length || settings.minChars == 0) { + if(selected_token) { + deselect_token($(selected_token), POSITION.AFTER); + } + + if(query.length >= settings.minChars) { + show_dropdown_searching(); + clearTimeout(timeout); + + timeout = setTimeout(function(){ + run_search(query); + }, settings.searchDelay); + } else { + hide_dropdown(); + } + } + } + + // Do the actual search + function run_search(query) { + var cache_key = query + computeURL(); + var cached_results = cache.get(cache_key); + if(cached_results) { + populate_dropdown(query, cached_results); + } else { + // Are we doing an ajax search or local data search? + if(settings.url) { + var url = computeURL(); + // Extract exisiting get params + var ajax_params = {}; + ajax_params.data = {}; + if(url.indexOf("?") > -1) { + var parts = url.split("?"); + ajax_params.url = parts[0]; + + var param_array = parts[1].split("&"); + $.each(param_array, function (index, value) { + var kv = value.split("="); + ajax_params.data[kv[0]] = kv[1]; + }); + } else { + ajax_params.url = url; + } + + // Prepare the request + ajax_params.data[settings.queryParam] = query; + ajax_params.type = settings.method; + ajax_params.dataType = settings.contentType; + if(settings.crossDomain) { + ajax_params.dataType = "jsonp"; + } + + // Attach the success callback + ajax_params.success = function(results) { + if($.isFunction(settings.onResult)) { + results = settings.onResult.call(hidden_input, results); + } + cache.add(cache_key, settings.jsonContainer ? results[settings.jsonContainer] : results); + + // only populate the dropdown if the results are associated with the active search query + if(input_box.val().toLowerCase() === query) { + populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results); + } + }; + + // Make the request + $.ajax(ajax_params); + } else if(settings.search_function){ + settings.search_function(query, function(results){ + cache.add(cache_key, results); + populate_dropdown(query, results); + }); + } else if(settings.local_data) { + // Do the search through local data + var results = $.grep(settings.local_data, function (row) { + return row[settings.propertyToSearch].toLowerCase().indexOf(query.toLowerCase()) > -1; + }); + + if($.isFunction(settings.onResult)) { + results = settings.onResult.call(hidden_input, results); + } + cache.add(cache_key, results); + populate_dropdown(query, results); + } + } + } + + // compute the dynamic URL + function computeURL() { + var url = settings.url; + if(typeof settings.url == 'function') { + url = settings.url.call(); + } + return url; + } +}; + +// Really basic cache for the results +$.TokenList.Cache = function (options) { + var settings = $.extend({ + max_size: 500 + }, options); + + var data = {}; + var size = 0; + + var flush = function () { + data = {}; + size = 0; + }; + + this.add = function (query, results) { + if(size > settings.max_size) { + flush(); + } + + if(!data[query]) { + size += 1; + } + + data[query] = results; + }; + + this.get = function (query) { + return data[query]; + }; +}; +}(jQuery));