diff --git a/browser/base/content/test/favicons/browser_preferred_icons.js b/browser/base/content/test/favicons/browser_preferred_icons.js index 455d260e893c..a26b6a891706 100644 --- a/browser/base/content/test/favicons/browser_preferred_icons.js +++ b/browser/base/content/test/favicons/browser_preferred_icons.js @@ -4,6 +4,9 @@ const ROOT = "http://mochi.test:8888/browser/browser/base/content/test/favicons/"; function waitIcon(url) { + // Make sure we don't miss out on an icon if it was previously used in a test + PlacesUtils.favicons.removeFailedFavicon(makeURI(url)); + // Because there is debounce logic in ContentLinkHandler.jsm to reduce the // favicon loads, we have to wait some time before checking that icon was // stored properly. @@ -24,7 +27,8 @@ function createLinks(linkInfos) { let link = doc.createElement("link"); link.rel = "icon"; link.href = l.href; - link.type = l.type; + if (l.type) + link.type = l.type; if (l.size) link.setAttribute("sizes", `${l.size}x${l.size}`); head.appendChild(link); @@ -78,7 +82,7 @@ add_task(async function prefer_sized() { Assert.ok(true, "The expected icon has been set"); }); -add_task(async function prefer_ico() { +add_task(async function prefer_last_ico() { let promise = waitIcon(ROOT + "icon2.ico"); await createLinks([ { href: ROOT + "icon.ico", @@ -88,8 +92,73 @@ add_task(async function prefer_ico() { type: "image/png", }, { href: ROOT + "icon2.ico", - type: "image/x-icon" - }, + type: "image/x-icon" + }, + ]); + await promise; + // Must have at least one test. + Assert.ok(true, "The expected icon has been set"); +}); + +add_task(async function fuzzy_ico() { + let promise = waitIcon(ROOT + "microsoft.ico"); + await createLinks([ + { href: ROOT + "icon.ico", + type: "image/x-icon" + }, + { href: ROOT + "icon.png", + type: "image/png", + }, + { href: ROOT + "microsoft.ico", + type: "image/vnd.microsoft.icon" + }, + ]); + await promise; + // Must have at least one test. + Assert.ok(true, "The expected icon has been set"); +}); + +add_task(async function guess_svg() { + let promise = waitIcon(ROOT + "icon.svg"); + await createLinks([ + { href: ROOT + "icon.svg" }, + { href: ROOT + "icon.png", + type: "image/png", + size: 16 * Math.ceil(window.devicePixelRatio) + }, + { href: ROOT + "icon.ico", + type: "image/x-icon" + }, + ]); + await promise; + // Must have at least one test. + Assert.ok(true, "The expected icon has been set"); +}); + +add_task(async function guess_ico() { + let promise = waitIcon(ROOT + "icon.ico"); + await createLinks([ + { href: ROOT + "icon.ico" }, + { href: ROOT + "icon.png", + type: "image/png", + }, + ]); + await promise; + // Must have at least one test. + Assert.ok(true, "The expected icon has been set"); +}); + +add_task(async function guess_invalid() { + let promise = waitIcon(ROOT + "icon.svg"); + // Create strange links to make sure they don't break us + await createLinks([ + { href: ROOT + "icon.svg" }, + { href: ROOT + "icon" }, + { href: ROOT + "icon?.svg" }, + { href: ROOT + "icon#.svg" }, + { href: "data:text/plain,icon" }, + { href: "file:///icon" }, + { href: "about:icon" }, ]); await promise; // Must have at least one test. diff --git a/browser/components/extensions/ext-tabs.js b/browser/components/extensions/ext-tabs.js index 4c541dfd5a60..9abab56a7b4f 100644 --- a/browser/components/extensions/ext-tabs.js +++ b/browser/components/extensions/ext-tabs.js @@ -617,6 +617,12 @@ this.tabs = class extends ExtensionAPI { let window = destinationWindow || nativeTab.ownerGlobal; let gBrowser = window.gBrowser; + // If we are not moving the tab to a different window, and the window + // only has one tab, do nothing. + if (nativeTab.ownerGlobal == window && gBrowser.tabs.length === 1) { + continue; + } + let insertionPoint = indexMap.get(window) || moveProperties.index; // If the index is -1 it should go to the end of the tabs. if (insertionPoint == -1) { diff --git a/browser/components/newtab/aboutNewTabService.js b/browser/components/newtab/aboutNewTabService.js index 8128f70bbb15..96f6aefb8588 100644 --- a/browser/components/newtab/aboutNewTabService.js +++ b/browser/components/newtab/aboutNewTabService.js @@ -21,7 +21,7 @@ const TOPIC_LOCALES_CHANGE = "intl:requested-locales-changed"; // Automated tests ensure packaged locales are in this list. Copied output of: // https://github.com/mozilla/activity-stream/blob/master/bin/render-activity-stream-html.js -const ACTIVITY_STREAM_LOCALES = new Set("en-US ach ar ast az be bg bn-BD bn-IN br bs ca cak cs cy da de dsb el en-GB eo es-AR es-CL es-ES es-MX et eu fa ff fi fr fy-NL ga-IE gd gl gu-IN he hi-IN hr hsb hu hy-AM ia id it ja ka kab kk km kn ko lij lo lt ltg lv mk ml mr ms my nb-NO ne-NP nl nn-NO pa-IN pl pt-BR pt-PT rm ro ru sk sl sq sr sv-SE ta te th tl tr uk ur uz vi zh-CN zh-TW".split(" ")); +const ACTIVITY_STREAM_LOCALES = new Set("en-US ach ar ast az be bg bn-BD bn-IN br bs ca cak cs cy da de dsb el en-GB eo es-AR es-CL es-ES es-MX et eu fa ff fi fr fy-NL ga-IE gd gl gu-IN he hi-IN hr hsb hu hy-AM ia id it ja ka kab kk km kn ko lij lo lt ltg lv mk ml mr ms my nb-NO ne-NP nl nn-NO pa-IN pl pt-BR pt-PT rm ro ru si sk sl sq sr sv-SE ta te th tl tr uk ur uz vi zh-CN zh-TW".split(" ")); const ABOUT_URL = "about:newtab"; diff --git a/browser/components/places/content/controller.js b/browser/components/places/content/controller.js index 2b96588009f3..3bc178bad857 100644 --- a/browser/components/places/content/controller.js +++ b/browser/components/places/content/controller.js @@ -10,19 +10,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"); -// XXXmano: we should move most/all of these constants to PlacesUtils -const ORGANIZER_ROOT_BOOKMARKS = "place:folder=BOOKMARKS_MENU&excludeItems=1&queryType=1"; - -// No change to the view, preserve current selection -const RELOAD_ACTION_NOTHING = 0; -// Inserting items new to the view, select the inserted rows -const RELOAD_ACTION_INSERT = 1; -// Removing items from the view, select the first item after the last selected -const RELOAD_ACTION_REMOVE = 2; -// Moving items within a view, don't treat the dropped items as additional -// rows. -const RELOAD_ACTION_MOVE = 3; - /** * Represents an insertion point within a container where we can insert * items. diff --git a/browser/extensions/activity-stream/bootstrap.js b/browser/extensions/activity-stream/bootstrap.js index def9f193194c..9e1c538a9354 100644 --- a/browser/extensions/activity-stream/bootstrap.js +++ b/browser/extensions/activity-stream/bootstrap.js @@ -181,7 +181,7 @@ this.startup = function startup(data, reason) { startupReason = reason; // Only start Activity Stream up when the browser UI is ready - if (Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup).startingUp) { + if (Services.startup.startingUp) { Services.obs.addObserver(observe, BROWSER_READY_NOTIFICATION); } else { // Handle manual install or automatic install after manual uninstall diff --git a/browser/extensions/activity-stream/common/Actions.jsm b/browser/extensions/activity-stream/common/Actions.jsm index 3ef773e67ed8..ee6d2b316bc7 100644 --- a/browser/extensions/activity-stream/common/Actions.jsm +++ b/browser/extensions/activity-stream/common/Actions.jsm @@ -69,8 +69,10 @@ for (const type of [ "SETTINGS_OPEN", "SET_PREF", "SHOW_FIREFOX_ACCOUNTS", + "SNIPPETS_BLOCKLIST_UPDATED", "SNIPPETS_DATA", "SNIPPETS_RESET", + "SNIPPET_BLOCKED", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", diff --git a/browser/extensions/activity-stream/css/activity-stream-linux.css b/browser/extensions/activity-stream/css/activity-stream-linux.css index f67deb8bc67a..22cc02398b0f 100644 --- a/browser/extensions/activity-stream/css/activity-stream-linux.css +++ b/browser/extensions/activity-stream/css/activity-stream-linux.css @@ -23,15 +23,15 @@ input { display: none !important; } .icon { - display: inline-block; - width: 16px; - height: 16px; - background-size: 16px; background-position: center center; background-repeat: no-repeat; - vertical-align: middle; + background-size: 16px; + -moz-context-properties: fill; + display: inline-block; fill: rgba(12, 12, 13, 0.8); - -moz-context-properties: fill; } + height: 16px; + vertical-align: middle; + width: 16px; } .icon.icon-spacer { margin-inline-end: 8px; } .icon.icon-small-spacer { @@ -44,9 +44,9 @@ input { background-image: url("../data/content/assets/glyph-delete-16.svg"); } .icon.icon-modal-delete { background-image: url("../data/content/assets/glyph-modal-delete-32.svg"); - width: 32px; + background-size: 32px; height: 32px; - background-size: 32px; } + width: 32px; } .icon.icon-dismiss { background-image: url("../data/content/assets/glyph-dismiss-16.svg"); } .icon.icon-info { @@ -71,8 +71,7 @@ input { background-image: url("../data/content/assets/glyph-historyItem-16.svg"); } .icon.icon-trending { background-image: url("../data/content/assets/glyph-trending-16.svg"); - transform: translateY(2px); - /* trending bolt is visually top heavy */ } + transform: translateY(2px); } .icon.icon-now { background-image: url("chrome://browser/skin/history.svg"); } .icon.icon-topsites { @@ -125,24 +124,24 @@ a { color: #008EA4; } .sr-only { - position: absolute; - width: 1px; + border: 0; + clip: rect(0, 0, 0, 0); height: 1px; - padding: 0; margin: -1px; overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; } + padding: 0; + position: absolute; + width: 1px; } .inner-border { border: 1px solid #D7D7DB; border-radius: 3px; + height: 100%; + left: 0; + pointer-events: none; position: absolute; top: 0; - left: 0; width: 100%; - height: 100%; - pointer-events: none; z-index: 100; } @keyframes fadeIn { @@ -155,25 +154,25 @@ a { opacity: 0; transition: opacity 0.2s ease-in; } .show-on-init.on { - opacity: 1; - animation: fadeIn 0.2s; } + animation: fadeIn 0.2s; + opacity: 1; } .actions { border-top: 1px solid #D7D7DB; display: flex; flex-direction: row; - margin: 0; - padding: 15px 25px 0 25px; + flex-wrap: wrap; justify-content: flex-start; - flex-wrap: wrap; } + margin: 0; + padding: 15px 25px 0; } .actions button { background-color: #F9F9FA; border: 1px solid #B1B1B3; border-radius: 4px; color: inherit; cursor: pointer; - padding: 10px 30px; margin-bottom: 15px; + padding: 10px 30px; white-space: nowrap; } .actions button:hover:not(.dismiss) { box-shadow: 0 0 0 5px #D7D7DB; @@ -193,16 +192,16 @@ a { .outer-wrapper { display: flex; - padding: 40px 32px 32px; + flex-grow: 1; height: 100%; - flex-grow: 1; } + padding: 40px 32px 32px; } .outer-wrapper.fixed-to-top { height: auto; } main { margin: auto; - width: 224px; - padding-bottom: 48px; } + padding-bottom: 48px; + width: 224px; } @media (min-width: 416px) { main { width: 352px; } } @@ -245,49 +244,50 @@ main { list-style: none; margin: 0; margin-bottom: -18px; - padding: 0; - margin-inline-end: -32px; } + margin-inline-end: -32px; + padding: 0; } @media (max-width: 416px) { .top-sites-list :nth-child(2n+1) .context-menu { - margin-inline-start: auto; margin-inline-end: auto; - offset-inline-start: -32px; - offset-inline-end: auto; } + margin-inline-start: auto; + offset-inline-end: auto; + offset-inline-start: -32px; } .top-sites-list :nth-child(2n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 416px) and (max-width: 544px) { - .top-sites-list :nth-child(3n+2) .context-menu, .top-sites-list :nth-child(3n) .context-menu { - margin-inline-start: auto; + .top-sites-list :nth-child(3n+2) .context-menu, + .top-sites-list :nth-child(3n) .context-menu { margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 544px) and (max-width: 800px) { .top-sites-list :nth-child(4n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 544px) and (max-width: 768px) { .top-sites-list :nth-child(4n+3) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 800px) and (max-width: 1248px) { .top-sites-list :nth-child(6n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 800px) and (max-width: 1024px) { .top-sites-list :nth-child(6n+5) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } .top-sites-list li { display: inline-block; margin: 0 0 8px; @@ -295,53 +295,56 @@ main { .top-sites-list .top-site-outer { position: relative; } .top-sites-list .top-site-outer > a { - display: block; color: inherit; + display: block; outline: none; } - .top-sites-list .top-site-outer > a.active .tile, .top-sites-list .top-site-outer > a:focus .tile { + .top-sites-list .top-site-outer > a:-moz-any(.active, :focus) .tile { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px #D7D7DB; transition: box-shadow 150ms; } .top-sites-list .top-site-outer .context-menu-button { - cursor: pointer; - position: absolute; - top: -13.5px; - offset-inline-end: -13.5px; - width: 27px; - height: 27px; + background-clip: padding-box; background-color: #FFF; background-image: url("chrome://browser/skin/page-action.svg"); background-position: 55%; - background-clip: padding-box; border: 1px solid #B1B1B3; border-radius: 100%; box-shadow: 0 2px rgba(12, 12, 13, 0.1); + cursor: pointer; fill: rgba(12, 12, 13, 0.8); - transform: scale(0.25); + height: 27px; + offset-inline-end: -13.5px; opacity: 0; + position: absolute; + top: -13.5px; + transform: scale(0.25); + transition-duration: 200ms; transition-property: transform, opacity; - transition-duration: 200ms; } - .top-sites-list .top-site-outer .context-menu-button:focus, .top-sites-list .top-site-outer .context-menu-button:active { - transform: scale(1); - opacity: 1; } - .top-sites-list .top-site-outer:hover .tile, .top-sites-list .top-site-outer:focus .tile, .top-sites-list .top-site-outer.active .tile { + width: 27px; } + .top-sites-list .top-site-outer .context-menu-button:-moz-any(:active, :focus) { + opacity: 1; + transform: scale(1); } + .top-sites-list .top-site-outer:-moz-any(.active, :focus, :hover) .tile { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px #D7D7DB; transition: box-shadow 150ms; } - .top-sites-list .top-site-outer:hover .context-menu-button, .top-sites-list .top-site-outer:focus .context-menu-button, .top-sites-list .top-site-outer.active .context-menu-button { - transform: scale(1); - opacity: 1; } + .top-sites-list .top-site-outer:-moz-any(.active, :focus, :hover) .edit-menu { + opacity: 1; + transform: scale(1); } + .top-sites-list .top-site-outer:-moz-any(.active, :focus, :hover) .context-menu-button { + opacity: 1; + transform: scale(1); } .top-sites-list .top-site-outer .tile { - position: relative; - height: 96px; - width: 96px; border-radius: 6px; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 1px 4px 0 rgba(12, 12, 13, 0.1); - color: #737373; - font-weight: 200; - font-size: 32px; - text-transform: uppercase; - display: flex; + height: 96px; + position: relative; + width: 96px; align-items: center; - justify-content: center; } + color: #737373; + display: flex; + font-size: 32px; + font-weight: 200; + justify-content: center; + text-transform: uppercase; } .top-sites-list .top-site-outer .tile::before { content: attr(data-fallback); } .top-sites-list .top-site-outer.placeholder .tile { @@ -349,43 +352,43 @@ main { .top-sites-list .top-site-outer.placeholder .screenshot { display: none; } .top-sites-list .top-site-outer .screenshot { - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; background-color: #FFF; + background-position: top left; + background-size: cover; border-radius: 6px; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); - background-size: cover; - background-position: top left; + height: 100%; + left: 0; + opacity: 0; + position: absolute; + top: 0; transition: opacity 1s; - opacity: 0; } + width: 100%; } .top-sites-list .top-site-outer .screenshot.active { opacity: 1; } .top-sites-list .top-site-outer .top-site-icon { - position: absolute; - border-radius: 6px; - box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); + background-color: #F9F9FA; background-position: center center; background-repeat: no-repeat; - background-color: #F9F9FA; } + border-radius: 6px; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); + position: absolute; } .top-sites-list .top-site-outer .rich-icon { - top: 0; - offset-inline-start: 0; + background-size: 96px; height: 100%; - width: 100%; - background-size: 96px; } + offset-inline-start: 0; + top: 0; + width: 100%; } .top-sites-list .top-site-outer .default-icon { + background-size: 32px; bottom: -6px; height: 42px; offset-inline-end: -6px; width: 42px; - background-size: 32px; - display: flex; align-items: center; - justify-content: center; - font-size: 20px; } + display: flex; + font-size: 20px; + justify-content: center; } .top-sites-list .top-site-outer .default-icon[data-fallback]::before { content: attr(data-fallback); } .top-sites-list .top-site-outer .title { @@ -423,7 +426,7 @@ main { transition-property: transform, opacity; transition-duration: 200ms; z-index: 1000; } - .top-sites-list .top-site-outer .edit-menu:focus, .top-sites-list .top-site-outer .edit-menu:active { + .top-sites-list .top-site-outer .edit-menu:-moz-any(:active, :focus) { transform: scale(1); opacity: 1; } .top-sites-list .top-site-outer .edit-menu button { @@ -443,9 +446,6 @@ main { border-right: 0; } .top-sites-list .top-site-outer .edit-menu button:first-child:dir(rtl) { border-right: 0; } - .top-sites-list .top-site-outer:hover .edit-menu, .top-sites-list .top-site-outer:focus .edit-menu, .top-sites-list .top-site-outer.active .edit-menu { - transform: scale(1); - opacity: 1; } .edit-topsites-wrapper .edit-topsites-button { border-right: 1px solid #D7D7DB; @@ -460,7 +460,7 @@ main { .edit-topsites-wrapper .edit-topsites-button:dir(rtl) { border-left: 1px solid #D7D7DB; border-right: 0; } - .edit-topsites-wrapper .edit-topsites-button:focus, .edit-topsites-wrapper .edit-topsites-button:active { + .edit-topsites-wrapper .edit-topsites-button:-moz-any(:active, :focus) { opacity: 1; } .edit-topsites-wrapper .edit-topsites-button button { background: none; @@ -499,7 +499,7 @@ main { .edit-topsites-wrapper .show-less span { padding-inline-start: 3px; } -section.top-sites:not(.collapsed):hover .edit-topsites-button { +.top-sites:not(.collapsed):hover .edit-topsites-button { opacity: 1; pointer-events: auto; } @@ -564,59 +564,59 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { transform: translateY(0); } } .sections-list .section-list { - margin: 0; display: grid; + grid-gap: 32px; grid-template-columns: repeat(auto-fit, 224px); - grid-gap: 32px; } + margin: 0; } @media (max-width: 544px) { .sections-list .section-list .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 544px) and (max-width: 800px) { .sections-list .section-list :nth-child(2n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 800px) and (max-width: 1248px) { .sections-list .section-list :nth-child(3n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } .sections-list .section-empty-state { - width: 100%; - height: 266px; - display: flex; border: 1px solid #D7D7DB; - border-radius: 3px; } + border-radius: 3px; + display: flex; + height: 266px; + width: 100%; } .sections-list .section-empty-state .empty-state { margin: auto; max-width: 350px; } .sections-list .section-empty-state .empty-state .empty-state-icon { - background-size: 50px 50px; - background-repeat: no-repeat; background-position: center; - fill: rgba(12, 12, 13, 0.6); + background-repeat: no-repeat; + background-size: 50px 50px; -moz-context-properties: fill; + display: block; + fill: rgba(12, 12, 13, 0.6); height: 50px; - width: 50px; margin: 0 auto; - display: block; } + width: 50px; } .sections-list .section-empty-state .empty-state .empty-state-message { - margin-bottom: 0; - font-size: 13px; color: #737373; + font-size: 13px; + margin-bottom: 0; text-align: center; } .topic { - font-size: 12px; color: #737373; - margin-top: 12px; - line-height: 1.6; } + font-size: 12px; + line-height: 1.6; + margin-top: 12px; } @media (min-width: 800px) { .topic { line-height: 16px; } } @@ -656,27 +656,27 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .topic .topic-read-more:dir(rtl)::after { transform: scaleX(-1); } .topic::after { - content: ""; - display: table; - clear: both; } + clear: both; + content: ''; + display: table; } .search-wrapper { cursor: default; display: flex; - position: relative; + height: 35px; margin: 1px 1px 40px; - width: 100%; - height: 35px; } + position: relative; + width: 100%; } .search-wrapper input { - border: none; + border: 0; border-radius: 3px; box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.15); color: inherit; + font-size: 15px; padding: 0; padding-inline-end: 36px; padding-inline-start: 35px; - width: 100%; - font-size: 15px; } + width: 100%; } .search-wrapper:hover input { box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.25); } .search-wrapper:active input, @@ -684,23 +684,23 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { box-shadow: 0 0 0 3px #0A84FF; } .search-wrapper .search-label { background: url("chrome://browser/skin/search-glass.svg") no-repeat 12px center/16px; - fill: rgba(12, 12, 13, 0.4); -moz-context-properties: fill; - position: absolute; - offset-inline-start: 0; + fill: rgba(12, 12, 13, 0.4); height: 100%; + offset-inline-start: 0; + position: absolute; width: 35px; } .search-wrapper .search-button { background: url("chrome://browser/skin/forward.svg") no-repeat center center; - border-radius: 0 3px 3px 0; - border: 0; - width: 36px; - fill: rgba(12, 12, 13, 0.4); - -moz-context-properties: fill; background-size: 16px 16px; + border: 0; + border-radius: 0 3px 3px 0; + -moz-context-properties: fill; + fill: rgba(12, 12, 13, 0.4); height: 100%; offset-inline-end: 0; - position: absolute; } + position: absolute; + width: 36px; } .search-wrapper .search-button:focus, .search-wrapper .search-button:hover { background-color: rgba(12, 12, 13, 0.1); cursor: pointer; } @@ -713,43 +713,43 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { transform: translateY(2px); } .context-menu { - display: block; - position: absolute; - font-size: 14px; - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 0, 0, 0.2); - top: 6.75px; - offset-inline-start: 100%; - margin-inline-start: 5px; - z-index: 10000; background: #F9F9FA; - border-radius: 5px; } + border-radius: 5px; + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 0, 0, 0.2); + display: block; + font-size: 14px; + margin-inline-start: 5px; + offset-inline-start: 100%; + position: absolute; + top: 6.75px; + z-index: 10000; } .context-menu > ul { + list-style: none; margin: 0; - padding: 5px 0; - list-style: none; } + padding: 5px 0; } .context-menu > ul > li { margin: 0; width: 100%; } .context-menu > ul > li.separator { - margin: 5px 0; - border-bottom: 1px solid rgba(0, 0, 0, 0.2); } + border-bottom: 1px solid rgba(0, 0, 0, 0.2); + margin: 5px 0; } .context-menu > ul > li > a { - outline: none; - cursor: pointer; + align-items: center; color: inherit; - white-space: nowrap; - padding: 3px 12px; - line-height: 16px; + cursor: pointer; display: flex; - align-items: center; } - .context-menu > ul > li > a:hover, .context-menu > ul > li > a:focus { + line-height: 16px; + outline: none; + padding: 3px 12px; + white-space: nowrap; } + .context-menu > ul > li > a:-moz-any(:focus, :hover) { background: #0060DF; color: #FFF; } - .context-menu > ul > li > a:hover a, .context-menu > ul > li > a:focus a { + .context-menu > ul > li > a:-moz-any(:focus, :hover) a { color: #0C0C0D; } - .context-menu > ul > li > a:hover .icon, .context-menu > ul > li > a:focus .icon { + .context-menu > ul > li > a:-moz-any(:focus, :hover) .icon { fill: #FFF; } - .context-menu > ul > li > a:hover:hover, .context-menu > ul > li > a:hover:focus, .context-menu > ul > li > a:focus:hover, .context-menu > ul > li > a:focus:focus { + .context-menu > ul > li > a:-moz-any(:focus, :hover):-moz-any(:focus, :hover) { color: #FFF; } .prefs-pane { @@ -859,14 +859,14 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .prefs-pane [type='checkbox']:checked + label::after { background: url("chrome://global/skin/in-content/check.svg") no-repeat center center; content: ''; + -moz-context-properties: fill, stroke; + fill: #0060DF; height: 21px; offset-inline-start: 0; position: absolute; + stroke: none; top: 0; - width: 21px; - -moz-context-properties: fill, stroke; - fill: #0060DF; - stroke: none; } + width: 21px; } .prefs-pane [type='checkbox']:not(:checked) + label::after { opacity: 0; } .prefs-pane [type='checkbox']:checked + label::after { @@ -882,9 +882,9 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { border: 0; cursor: pointer; fill: rgba(12, 12, 13, 0.6); + offset-inline-end: 15px; padding: 15px; position: fixed; - offset-inline-end: 15px; top: 15px; z-index: 12001; } .prefs-pane-button button:hover { @@ -893,35 +893,35 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { background-color: #F9F9FA; } .confirmation-dialog .modal { - position: fixed; - width: 400px; - top: 20%; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1); left: 50%; margin-left: -200px; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.08); } + position: fixed; + top: 20%; + width: 400px; } .confirmation-dialog section { margin: 0; } .confirmation-dialog .modal-message { + display: flex; padding: 16px; - padding-bottom: 0; - display: flex; } + padding-bottom: 0; } .confirmation-dialog .modal-message p { margin: 0; margin-bottom: 16px; } .confirmation-dialog .actions { - padding: 0px 16px 0 16px; - border: none; + border: 0; + display: flex; flex-wrap: nowrap; - display: flex; } + padding: 0 16px; } .confirmation-dialog .actions button { margin-inline-end: 16px; width: 50%; } .confirmation-dialog .actions button.done { - margin-inline-start: 0; - margin-inline-end: 0; } + margin-inline-end: 0; + margin-inline-start: 0; } .confirmation-dialog .icon { margin-inline-end: 16px; } @@ -945,83 +945,83 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .card-outer { background: #FFF; - display: inline-block; - margin-inline-end: 32px; - width: 224px; border-radius: 3px; + display: inline-block; height: 266px; - position: relative; } + margin-inline-end: 32px; + position: relative; + width: 224px; } .card-outer .context-menu-button { - cursor: pointer; - position: absolute; - top: -13.5px; - offset-inline-end: -13.5px; - width: 27px; - height: 27px; + background-clip: padding-box; background-color: #FFF; background-image: url("chrome://browser/skin/page-action.svg"); background-position: 55%; - background-clip: padding-box; border: 1px solid #B1B1B3; border-radius: 100%; box-shadow: 0 2px rgba(12, 12, 13, 0.1); + cursor: pointer; fill: rgba(12, 12, 13, 0.8); - transform: scale(0.25); + height: 27px; + offset-inline-end: -13.5px; opacity: 0; + position: absolute; + top: -13.5px; + transform: scale(0.25); + transition-duration: 200ms; transition-property: transform, opacity; - transition-duration: 200ms; } - .card-outer .context-menu-button:focus, .card-outer .context-menu-button:active { - transform: scale(1); - opacity: 1; } + width: 27px; } + .card-outer .context-menu-button:-moz-any(:active, :focus) { + opacity: 1; + transform: scale(1); } .card-outer.placeholder { background: transparent; } .card-outer.placeholder .card { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); } .card-outer .card { - height: 100%; border-radius: 3px; - box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); } + box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); + height: 100%; } .card-outer > a { - display: block; color: inherit; + display: block; height: 100%; outline: none; position: absolute; width: 224px; } - .card-outer > a.active .card, .card-outer > a:focus .card { + .card-outer > a:-moz-any(.active, :focus) .card { box-shadow: 0 0 0 5px #D7D7DB; transition: box-shadow 150ms; } - .card-outer > a.active .card-title, .card-outer > a:focus .card-title { + .card-outer > a:-moz-any(.active, :focus) .card-title { color: #0060DF; } .card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) { - outline: none; box-shadow: 0 0 0 5px #D7D7DB; - transition: box-shadow 150ms; } + transition: box-shadow 150ms; + outline: none; } .card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) .context-menu-button { - transform: scale(1); - opacity: 1; } + opacity: 1; + transform: scale(1); } .card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) .card-title { color: #0060DF; } .card-outer .card-preview-image-outer { background-color: #F9F9FA; - position: relative; - height: 122px; border-radius: 3px 3px 0 0; - overflow: hidden; } + height: 122px; + overflow: hidden; + position: relative; } .card-outer .card-preview-image-outer::after { border-bottom: 1px solid rgba(0, 0, 0, 0.05); bottom: 0; - content: " "; + content: ''; position: absolute; width: 100%; } .card-outer .card-preview-image-outer .card-preview-image { - width: 100%; - height: 100%; - background-size: cover; background-position: center; background-repeat: no-repeat; + background-size: cover; + height: 100%; opacity: 0; - transition: opacity 1s cubic-bezier(0.07, 0.95, 0, 1); } + transition: opacity 1s cubic-bezier(0.07, 0.95, 0, 1); + width: 100%; } .card-outer .card-preview-image-outer .card-preview-image.loaded { opacity: 1; } .card-outer .card-details { @@ -1029,8 +1029,8 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .card-outer .card-details.no-image { padding-top: 16px; } .card-outer .card-text { - overflow: hidden; - max-height: 78px; } + max-height: 78px; + overflow: hidden; } .card-outer .card-text.no-image { max-height: 192px; } .card-outer .card-text.no-host-name, .card-outer .card-text.no-context { @@ -1047,30 +1047,30 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .card-outer .card-host-name { color: #737373; font-size: 10px; - padding-bottom: 4px; - text-transform: uppercase; overflow: hidden; - text-overflow: ellipsis; } + padding-bottom: 4px; + text-overflow: ellipsis; + text-transform: uppercase; } .card-outer .card-title { - margin: 0 0 2px; font-size: 14px; - word-wrap: break-word; - line-height: 19px; } + line-height: 19px; + margin: 0 0 2px; + word-wrap: break-word; } .card-outer .card-description { font-size: 12px; + line-height: 19px; margin: 0; - word-wrap: break-word; overflow: hidden; - line-height: 19px; } + word-wrap: break-word; } .card-outer .card-context { + bottom: 0; + color: #737373; + display: flex; + font-size: 11px; + left: 0; padding: 12px 16px 12px 14px; position: absolute; - bottom: 0; - left: 0; - right: 0; - color: #737373; - font-size: 11px; - display: flex; } + right: 0; } .card-outer .card-context-icon { fill: rgba(12, 12, 13, 0.6); margin-inline-end: 6px; } @@ -1103,13 +1103,13 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { display: none; } @media (min-width: 544px) { .manual-migration-container .icon { + align-self: center; display: block; fill: rgba(12, 12, 13, 0.6); - margin-inline-end: 6px; - align-self: center; } } + margin-inline-end: 6px; } } .manual-migration-actions { - border: none; + border: 0; display: block; flex-wrap: nowrap; } @media (min-width: 544px) { @@ -1131,8 +1131,8 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .collapsible-section .section-title .icon-arrowhead-down, .collapsible-section .section-title .icon-arrowhead-forward { - margin-top: -1px; - margin-inline-start: 8px; } + margin-inline-start: 8px; + margin-top: -1px; } .collapsible-section .section-top-bar { position: relative; } @@ -1142,30 +1142,36 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { top: 0; } .collapsible-section .section-top-bar .info-option-icon { background-image: url("../data/content/assets/glyph-info-option-12.svg"); - background-size: 12px 12px; - background-repeat: no-repeat; background-position: center; - fill: rgba(12, 12, 13, 0.6); + background-repeat: no-repeat; + background-size: 12px 12px; -moz-context-properties: fill; - height: 16px; - width: 16px; display: inline-block; + fill: rgba(12, 12, 13, 0.6); + height: 16px; margin-bottom: -2px; opacity: 0; - transition: opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); } - .collapsible-section .section-top-bar .info-option-icon:focus, .collapsible-section .section-top-bar .info-option-icon:active { + transition: opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); + width: 16px; } + .collapsible-section .section-top-bar .info-option-icon[aria-expanded='true'] { + background-color: rgba(12, 12, 13, 0.1); + border-radius: 1px; + box-shadow: 0 0 0 5px rgba(12, 12, 13, 0.1); + fill: rgba(12, 12, 13, 0.8); } + .collapsible-section .section-top-bar .info-option-icon[aria-expanded='true'] + .info-option { + opacity: 1; + transition: visibility 0.2s, opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); + visibility: visible; } + .collapsible-section .section-top-bar .info-option-icon:not([aria-expanded='true']) + .info-option { + pointer-events: none; } + .collapsible-section .section-top-bar .info-option-icon:-moz-any(:active, :focus) { opacity: 1; } - .collapsible-section .section-top-bar .info-option-icon[aria-expanded="true"] { - background-color: rgba(12, 12, 13, 0.1); - border-radius: 1px; - box-shadow: 0 0 0 5px rgba(12, 12, 13, 0.1); - fill: rgba(12, 12, 13, 0.8); } .collapsible-section .section-top-bar .section-info-option .info-option { - visibility: hidden; opacity: 0; - transition: visibility 0.2s, opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); } + transition: visibility 0.2s, opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); + visibility: hidden; } .collapsible-section .section-top-bar .section-info-option .info-option::after, .collapsible-section .section-top-bar .section-info-option .info-option::before { - content: ""; + content: ''; offset-inline-end: 0; position: absolute; } .collapsible-section .section-top-bar .section-info-option .info-option::before { @@ -1182,27 +1188,21 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { height: 10px; offset-inline-start: 0; top: -10px; } - .collapsible-section .section-top-bar .info-option-icon[aria-expanded="true"] + .info-option { - visibility: visible; - opacity: 1; - transition: visibility 0.2s, opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); } - .collapsible-section .section-top-bar .info-option-icon:not([aria-expanded="true"]) + .info-option { - pointer-events: none; } .collapsible-section .section-top-bar .info-option { - z-index: 9999; - position: absolute; background: #FFF; border: 1px solid #D7D7DB; border-radius: 3px; + box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); font-size: 13px; line-height: 120%; margin-inline-end: -9px; offset-inline-end: 0; - top: 26px; - width: 320px; padding: 24px; - box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); - -moz-user-select: none; } + position: absolute; + top: 26px; + -moz-user-select: none; + width: 320px; + z-index: 9999; } .collapsible-section .section-top-bar .info-option-header { font-size: 15px; font-weight: 600; } @@ -1215,8 +1215,8 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .collapsible-section .section-top-bar .info-option-manage { margin-top: 24px; } .collapsible-section .section-top-bar .info-option-manage button { - background: none; - border: none; + background: 0; + border: 0; color: #0060DF; cursor: pointer; margin: 0; @@ -1256,14 +1256,14 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { color: #008EA4; padding-left: 3px; } .collapsible-section .section-disclaimer button { - margin-top: 2px; - offset-inline-end: 0; - min-height: 26px; - max-width: 130px; background: #F9F9FA; border: 1px solid #B1B1B3; border-radius: 4px; - cursor: pointer; } + cursor: pointer; + margin-top: 2px; + max-width: 130px; + min-height: 26px; + offset-inline-end: 0; } .collapsible-section .section-disclaimer button:hover:not(.dismiss) { box-shadow: 0 0 0 5px #D7D7DB; transition: box-shadow 150ms; } diff --git a/browser/extensions/activity-stream/css/activity-stream-mac.css b/browser/extensions/activity-stream/css/activity-stream-mac.css index 2c6af371c916..f3202a48a6a7 100644 --- a/browser/extensions/activity-stream/css/activity-stream-mac.css +++ b/browser/extensions/activity-stream/css/activity-stream-mac.css @@ -23,15 +23,15 @@ input { display: none !important; } .icon { - display: inline-block; - width: 16px; - height: 16px; - background-size: 16px; background-position: center center; background-repeat: no-repeat; - vertical-align: middle; + background-size: 16px; + -moz-context-properties: fill; + display: inline-block; fill: rgba(12, 12, 13, 0.8); - -moz-context-properties: fill; } + height: 16px; + vertical-align: middle; + width: 16px; } .icon.icon-spacer { margin-inline-end: 8px; } .icon.icon-small-spacer { @@ -44,9 +44,9 @@ input { background-image: url("../data/content/assets/glyph-delete-16.svg"); } .icon.icon-modal-delete { background-image: url("../data/content/assets/glyph-modal-delete-32.svg"); - width: 32px; + background-size: 32px; height: 32px; - background-size: 32px; } + width: 32px; } .icon.icon-dismiss { background-image: url("../data/content/assets/glyph-dismiss-16.svg"); } .icon.icon-info { @@ -71,8 +71,7 @@ input { background-image: url("../data/content/assets/glyph-historyItem-16.svg"); } .icon.icon-trending { background-image: url("../data/content/assets/glyph-trending-16.svg"); - transform: translateY(2px); - /* trending bolt is visually top heavy */ } + transform: translateY(2px); } .icon.icon-now { background-image: url("chrome://browser/skin/history.svg"); } .icon.icon-topsites { @@ -125,24 +124,24 @@ a { color: #008EA4; } .sr-only { - position: absolute; - width: 1px; + border: 0; + clip: rect(0, 0, 0, 0); height: 1px; - padding: 0; margin: -1px; overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; } + padding: 0; + position: absolute; + width: 1px; } .inner-border { border: 1px solid #D7D7DB; border-radius: 3px; + height: 100%; + left: 0; + pointer-events: none; position: absolute; top: 0; - left: 0; width: 100%; - height: 100%; - pointer-events: none; z-index: 100; } @keyframes fadeIn { @@ -155,25 +154,25 @@ a { opacity: 0; transition: opacity 0.2s ease-in; } .show-on-init.on { - opacity: 1; - animation: fadeIn 0.2s; } + animation: fadeIn 0.2s; + opacity: 1; } .actions { border-top: 1px solid #D7D7DB; display: flex; flex-direction: row; - margin: 0; - padding: 15px 25px 0 25px; + flex-wrap: wrap; justify-content: flex-start; - flex-wrap: wrap; } + margin: 0; + padding: 15px 25px 0; } .actions button { background-color: #F9F9FA; border: 1px solid #B1B1B3; border-radius: 4px; color: inherit; cursor: pointer; - padding: 10px 30px; margin-bottom: 15px; + padding: 10px 30px; white-space: nowrap; } .actions button:hover:not(.dismiss) { box-shadow: 0 0 0 5px #D7D7DB; @@ -193,16 +192,16 @@ a { .outer-wrapper { display: flex; - padding: 40px 32px 32px; + flex-grow: 1; height: 100%; - flex-grow: 1; } + padding: 40px 32px 32px; } .outer-wrapper.fixed-to-top { height: auto; } main { margin: auto; - width: 224px; - padding-bottom: 48px; } + padding-bottom: 48px; + width: 224px; } @media (min-width: 416px) { main { width: 352px; } } @@ -245,49 +244,50 @@ main { list-style: none; margin: 0; margin-bottom: -18px; - padding: 0; - margin-inline-end: -32px; } + margin-inline-end: -32px; + padding: 0; } @media (max-width: 416px) { .top-sites-list :nth-child(2n+1) .context-menu { - margin-inline-start: auto; margin-inline-end: auto; - offset-inline-start: -32px; - offset-inline-end: auto; } + margin-inline-start: auto; + offset-inline-end: auto; + offset-inline-start: -32px; } .top-sites-list :nth-child(2n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 416px) and (max-width: 544px) { - .top-sites-list :nth-child(3n+2) .context-menu, .top-sites-list :nth-child(3n) .context-menu { - margin-inline-start: auto; + .top-sites-list :nth-child(3n+2) .context-menu, + .top-sites-list :nth-child(3n) .context-menu { margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 544px) and (max-width: 800px) { .top-sites-list :nth-child(4n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 544px) and (max-width: 768px) { .top-sites-list :nth-child(4n+3) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 800px) and (max-width: 1248px) { .top-sites-list :nth-child(6n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 800px) and (max-width: 1024px) { .top-sites-list :nth-child(6n+5) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } .top-sites-list li { display: inline-block; margin: 0 0 8px; @@ -295,53 +295,56 @@ main { .top-sites-list .top-site-outer { position: relative; } .top-sites-list .top-site-outer > a { - display: block; color: inherit; + display: block; outline: none; } - .top-sites-list .top-site-outer > a.active .tile, .top-sites-list .top-site-outer > a:focus .tile { + .top-sites-list .top-site-outer > a:-moz-any(.active, :focus) .tile { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px #D7D7DB; transition: box-shadow 150ms; } .top-sites-list .top-site-outer .context-menu-button { - cursor: pointer; - position: absolute; - top: -13.5px; - offset-inline-end: -13.5px; - width: 27px; - height: 27px; + background-clip: padding-box; background-color: #FFF; background-image: url("chrome://browser/skin/page-action.svg"); background-position: 55%; - background-clip: padding-box; border: 1px solid #B1B1B3; border-radius: 100%; box-shadow: 0 2px rgba(12, 12, 13, 0.1); + cursor: pointer; fill: rgba(12, 12, 13, 0.8); - transform: scale(0.25); + height: 27px; + offset-inline-end: -13.5px; opacity: 0; + position: absolute; + top: -13.5px; + transform: scale(0.25); + transition-duration: 200ms; transition-property: transform, opacity; - transition-duration: 200ms; } - .top-sites-list .top-site-outer .context-menu-button:focus, .top-sites-list .top-site-outer .context-menu-button:active { - transform: scale(1); - opacity: 1; } - .top-sites-list .top-site-outer:hover .tile, .top-sites-list .top-site-outer:focus .tile, .top-sites-list .top-site-outer.active .tile { + width: 27px; } + .top-sites-list .top-site-outer .context-menu-button:-moz-any(:active, :focus) { + opacity: 1; + transform: scale(1); } + .top-sites-list .top-site-outer:-moz-any(.active, :focus, :hover) .tile { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px #D7D7DB; transition: box-shadow 150ms; } - .top-sites-list .top-site-outer:hover .context-menu-button, .top-sites-list .top-site-outer:focus .context-menu-button, .top-sites-list .top-site-outer.active .context-menu-button { - transform: scale(1); - opacity: 1; } + .top-sites-list .top-site-outer:-moz-any(.active, :focus, :hover) .edit-menu { + opacity: 1; + transform: scale(1); } + .top-sites-list .top-site-outer:-moz-any(.active, :focus, :hover) .context-menu-button { + opacity: 1; + transform: scale(1); } .top-sites-list .top-site-outer .tile { - position: relative; - height: 96px; - width: 96px; border-radius: 6px; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 1px 4px 0 rgba(12, 12, 13, 0.1); - color: #737373; - font-weight: 200; - font-size: 32px; - text-transform: uppercase; - display: flex; + height: 96px; + position: relative; + width: 96px; align-items: center; - justify-content: center; } + color: #737373; + display: flex; + font-size: 32px; + font-weight: 200; + justify-content: center; + text-transform: uppercase; } .top-sites-list .top-site-outer .tile::before { content: attr(data-fallback); } .top-sites-list .top-site-outer.placeholder .tile { @@ -349,43 +352,43 @@ main { .top-sites-list .top-site-outer.placeholder .screenshot { display: none; } .top-sites-list .top-site-outer .screenshot { - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; background-color: #FFF; + background-position: top left; + background-size: cover; border-radius: 6px; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); - background-size: cover; - background-position: top left; + height: 100%; + left: 0; + opacity: 0; + position: absolute; + top: 0; transition: opacity 1s; - opacity: 0; } + width: 100%; } .top-sites-list .top-site-outer .screenshot.active { opacity: 1; } .top-sites-list .top-site-outer .top-site-icon { - position: absolute; - border-radius: 6px; - box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); + background-color: #F9F9FA; background-position: center center; background-repeat: no-repeat; - background-color: #F9F9FA; } + border-radius: 6px; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); + position: absolute; } .top-sites-list .top-site-outer .rich-icon { - top: 0; - offset-inline-start: 0; + background-size: 96px; height: 100%; - width: 100%; - background-size: 96px; } + offset-inline-start: 0; + top: 0; + width: 100%; } .top-sites-list .top-site-outer .default-icon { + background-size: 32px; bottom: -6px; height: 42px; offset-inline-end: -6px; width: 42px; - background-size: 32px; - display: flex; align-items: center; - justify-content: center; - font-size: 20px; } + display: flex; + font-size: 20px; + justify-content: center; } .top-sites-list .top-site-outer .default-icon[data-fallback]::before { content: attr(data-fallback); } .top-sites-list .top-site-outer .title { @@ -423,7 +426,7 @@ main { transition-property: transform, opacity; transition-duration: 200ms; z-index: 1000; } - .top-sites-list .top-site-outer .edit-menu:focus, .top-sites-list .top-site-outer .edit-menu:active { + .top-sites-list .top-site-outer .edit-menu:-moz-any(:active, :focus) { transform: scale(1); opacity: 1; } .top-sites-list .top-site-outer .edit-menu button { @@ -443,9 +446,6 @@ main { border-right: 0; } .top-sites-list .top-site-outer .edit-menu button:first-child:dir(rtl) { border-right: 0; } - .top-sites-list .top-site-outer:hover .edit-menu, .top-sites-list .top-site-outer:focus .edit-menu, .top-sites-list .top-site-outer.active .edit-menu { - transform: scale(1); - opacity: 1; } .edit-topsites-wrapper .edit-topsites-button { border-right: 1px solid #D7D7DB; @@ -460,7 +460,7 @@ main { .edit-topsites-wrapper .edit-topsites-button:dir(rtl) { border-left: 1px solid #D7D7DB; border-right: 0; } - .edit-topsites-wrapper .edit-topsites-button:focus, .edit-topsites-wrapper .edit-topsites-button:active { + .edit-topsites-wrapper .edit-topsites-button:-moz-any(:active, :focus) { opacity: 1; } .edit-topsites-wrapper .edit-topsites-button button { background: none; @@ -499,7 +499,7 @@ main { .edit-topsites-wrapper .show-less span { padding-inline-start: 3px; } -section.top-sites:not(.collapsed):hover .edit-topsites-button { +.top-sites:not(.collapsed):hover .edit-topsites-button { opacity: 1; pointer-events: auto; } @@ -564,59 +564,59 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { transform: translateY(0); } } .sections-list .section-list { - margin: 0; display: grid; + grid-gap: 32px; grid-template-columns: repeat(auto-fit, 224px); - grid-gap: 32px; } + margin: 0; } @media (max-width: 544px) { .sections-list .section-list .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 544px) and (max-width: 800px) { .sections-list .section-list :nth-child(2n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 800px) and (max-width: 1248px) { .sections-list .section-list :nth-child(3n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } .sections-list .section-empty-state { - width: 100%; - height: 266px; - display: flex; border: 1px solid #D7D7DB; - border-radius: 3px; } + border-radius: 3px; + display: flex; + height: 266px; + width: 100%; } .sections-list .section-empty-state .empty-state { margin: auto; max-width: 350px; } .sections-list .section-empty-state .empty-state .empty-state-icon { - background-size: 50px 50px; - background-repeat: no-repeat; background-position: center; - fill: rgba(12, 12, 13, 0.6); + background-repeat: no-repeat; + background-size: 50px 50px; -moz-context-properties: fill; + display: block; + fill: rgba(12, 12, 13, 0.6); height: 50px; - width: 50px; margin: 0 auto; - display: block; } + width: 50px; } .sections-list .section-empty-state .empty-state .empty-state-message { - margin-bottom: 0; - font-size: 13px; color: #737373; + font-size: 13px; + margin-bottom: 0; text-align: center; } .topic { - font-size: 12px; color: #737373; - margin-top: 12px; - line-height: 1.6; } + font-size: 12px; + line-height: 1.6; + margin-top: 12px; } @media (min-width: 800px) { .topic { line-height: 16px; } } @@ -656,27 +656,27 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .topic .topic-read-more:dir(rtl)::after { transform: scaleX(-1); } .topic::after { - content: ""; - display: table; - clear: both; } + clear: both; + content: ''; + display: table; } .search-wrapper { cursor: default; display: flex; - position: relative; + height: 35px; margin: 1px 1px 40px; - width: 100%; - height: 35px; } + position: relative; + width: 100%; } .search-wrapper input { - border: none; + border: 0; border-radius: 3px; box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.15); color: inherit; + font-size: 15px; padding: 0; padding-inline-end: 36px; padding-inline-start: 35px; - width: 100%; - font-size: 15px; } + width: 100%; } .search-wrapper:hover input { box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.25); } .search-wrapper:active input, @@ -684,23 +684,23 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { box-shadow: 0 0 0 3px #0A84FF; } .search-wrapper .search-label { background: url("chrome://browser/skin/search-glass.svg") no-repeat 12px center/16px; - fill: rgba(12, 12, 13, 0.4); -moz-context-properties: fill; - position: absolute; - offset-inline-start: 0; + fill: rgba(12, 12, 13, 0.4); height: 100%; + offset-inline-start: 0; + position: absolute; width: 35px; } .search-wrapper .search-button { background: url("chrome://browser/skin/forward.svg") no-repeat center center; - border-radius: 0 3px 3px 0; - border: 0; - width: 36px; - fill: rgba(12, 12, 13, 0.4); - -moz-context-properties: fill; background-size: 16px 16px; + border: 0; + border-radius: 0 3px 3px 0; + -moz-context-properties: fill; + fill: rgba(12, 12, 13, 0.4); height: 100%; offset-inline-end: 0; - position: absolute; } + position: absolute; + width: 36px; } .search-wrapper .search-button:focus, .search-wrapper .search-button:hover { background-color: rgba(12, 12, 13, 0.1); cursor: pointer; } @@ -713,43 +713,43 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { transform: translateY(2px); } .context-menu { - display: block; - position: absolute; - font-size: 14px; - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 0, 0, 0.2); - top: 6.75px; - offset-inline-start: 100%; - margin-inline-start: 5px; - z-index: 10000; background: #F9F9FA; - border-radius: 5px; } + border-radius: 5px; + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 0, 0, 0.2); + display: block; + font-size: 14px; + margin-inline-start: 5px; + offset-inline-start: 100%; + position: absolute; + top: 6.75px; + z-index: 10000; } .context-menu > ul { + list-style: none; margin: 0; - padding: 5px 0; - list-style: none; } + padding: 5px 0; } .context-menu > ul > li { margin: 0; width: 100%; } .context-menu > ul > li.separator { - margin: 5px 0; - border-bottom: 1px solid rgba(0, 0, 0, 0.2); } + border-bottom: 1px solid rgba(0, 0, 0, 0.2); + margin: 5px 0; } .context-menu > ul > li > a { - outline: none; - cursor: pointer; + align-items: center; color: inherit; - white-space: nowrap; - padding: 3px 12px; - line-height: 16px; + cursor: pointer; display: flex; - align-items: center; } - .context-menu > ul > li > a:hover, .context-menu > ul > li > a:focus { + line-height: 16px; + outline: none; + padding: 3px 12px; + white-space: nowrap; } + .context-menu > ul > li > a:-moz-any(:focus, :hover) { background: #0060DF; color: #FFF; } - .context-menu > ul > li > a:hover a, .context-menu > ul > li > a:focus a { + .context-menu > ul > li > a:-moz-any(:focus, :hover) a { color: #0C0C0D; } - .context-menu > ul > li > a:hover .icon, .context-menu > ul > li > a:focus .icon { + .context-menu > ul > li > a:-moz-any(:focus, :hover) .icon { fill: #FFF; } - .context-menu > ul > li > a:hover:hover, .context-menu > ul > li > a:hover:focus, .context-menu > ul > li > a:focus:hover, .context-menu > ul > li > a:focus:focus { + .context-menu > ul > li > a:-moz-any(:focus, :hover):-moz-any(:focus, :hover) { color: #FFF; } .prefs-pane { @@ -859,14 +859,14 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .prefs-pane [type='checkbox']:checked + label::after { background: url("chrome://global/skin/in-content/check.svg") no-repeat center center; content: ''; + -moz-context-properties: fill, stroke; + fill: #0060DF; height: 21px; offset-inline-start: 0; position: absolute; + stroke: none; top: 0; - width: 21px; - -moz-context-properties: fill, stroke; - fill: #0060DF; - stroke: none; } + width: 21px; } .prefs-pane [type='checkbox']:not(:checked) + label::after { opacity: 0; } .prefs-pane [type='checkbox']:checked + label::after { @@ -882,9 +882,9 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { border: 0; cursor: pointer; fill: rgba(12, 12, 13, 0.6); + offset-inline-end: 15px; padding: 15px; position: fixed; - offset-inline-end: 15px; top: 15px; z-index: 12001; } .prefs-pane-button button:hover { @@ -893,35 +893,35 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { background-color: #F9F9FA; } .confirmation-dialog .modal { - position: fixed; - width: 400px; - top: 20%; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1); left: 50%; margin-left: -200px; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.08); } + position: fixed; + top: 20%; + width: 400px; } .confirmation-dialog section { margin: 0; } .confirmation-dialog .modal-message { + display: flex; padding: 16px; - padding-bottom: 0; - display: flex; } + padding-bottom: 0; } .confirmation-dialog .modal-message p { margin: 0; margin-bottom: 16px; } .confirmation-dialog .actions { - padding: 0px 16px 0 16px; - border: none; + border: 0; + display: flex; flex-wrap: nowrap; - display: flex; } + padding: 0 16px; } .confirmation-dialog .actions button { margin-inline-end: 16px; width: 50%; } .confirmation-dialog .actions button.done { - margin-inline-start: 0; - margin-inline-end: 0; } + margin-inline-end: 0; + margin-inline-start: 0; } .confirmation-dialog .icon { margin-inline-end: 16px; } @@ -945,83 +945,83 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .card-outer { background: #FFF; - display: inline-block; - margin-inline-end: 32px; - width: 224px; border-radius: 3px; + display: inline-block; height: 266px; - position: relative; } + margin-inline-end: 32px; + position: relative; + width: 224px; } .card-outer .context-menu-button { - cursor: pointer; - position: absolute; - top: -13.5px; - offset-inline-end: -13.5px; - width: 27px; - height: 27px; + background-clip: padding-box; background-color: #FFF; background-image: url("chrome://browser/skin/page-action.svg"); background-position: 55%; - background-clip: padding-box; border: 1px solid #B1B1B3; border-radius: 100%; box-shadow: 0 2px rgba(12, 12, 13, 0.1); + cursor: pointer; fill: rgba(12, 12, 13, 0.8); - transform: scale(0.25); + height: 27px; + offset-inline-end: -13.5px; opacity: 0; + position: absolute; + top: -13.5px; + transform: scale(0.25); + transition-duration: 200ms; transition-property: transform, opacity; - transition-duration: 200ms; } - .card-outer .context-menu-button:focus, .card-outer .context-menu-button:active { - transform: scale(1); - opacity: 1; } + width: 27px; } + .card-outer .context-menu-button:-moz-any(:active, :focus) { + opacity: 1; + transform: scale(1); } .card-outer.placeholder { background: transparent; } .card-outer.placeholder .card { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); } .card-outer .card { - height: 100%; border-radius: 3px; - box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); } + box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); + height: 100%; } .card-outer > a { - display: block; color: inherit; + display: block; height: 100%; outline: none; position: absolute; width: 224px; } - .card-outer > a.active .card, .card-outer > a:focus .card { + .card-outer > a:-moz-any(.active, :focus) .card { box-shadow: 0 0 0 5px #D7D7DB; transition: box-shadow 150ms; } - .card-outer > a.active .card-title, .card-outer > a:focus .card-title { + .card-outer > a:-moz-any(.active, :focus) .card-title { color: #0060DF; } .card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) { - outline: none; box-shadow: 0 0 0 5px #D7D7DB; - transition: box-shadow 150ms; } + transition: box-shadow 150ms; + outline: none; } .card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) .context-menu-button { - transform: scale(1); - opacity: 1; } + opacity: 1; + transform: scale(1); } .card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) .card-title { color: #0060DF; } .card-outer .card-preview-image-outer { background-color: #F9F9FA; - position: relative; - height: 122px; border-radius: 3px 3px 0 0; - overflow: hidden; } + height: 122px; + overflow: hidden; + position: relative; } .card-outer .card-preview-image-outer::after { border-bottom: 1px solid rgba(0, 0, 0, 0.05); bottom: 0; - content: " "; + content: ''; position: absolute; width: 100%; } .card-outer .card-preview-image-outer .card-preview-image { - width: 100%; - height: 100%; - background-size: cover; background-position: center; background-repeat: no-repeat; + background-size: cover; + height: 100%; opacity: 0; - transition: opacity 1s cubic-bezier(0.07, 0.95, 0, 1); } + transition: opacity 1s cubic-bezier(0.07, 0.95, 0, 1); + width: 100%; } .card-outer .card-preview-image-outer .card-preview-image.loaded { opacity: 1; } .card-outer .card-details { @@ -1029,8 +1029,8 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .card-outer .card-details.no-image { padding-top: 16px; } .card-outer .card-text { - overflow: hidden; - max-height: 78px; } + max-height: 78px; + overflow: hidden; } .card-outer .card-text.no-image { max-height: 192px; } .card-outer .card-text.no-host-name, .card-outer .card-text.no-context { @@ -1047,30 +1047,30 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .card-outer .card-host-name { color: #737373; font-size: 10px; - padding-bottom: 4px; - text-transform: uppercase; overflow: hidden; - text-overflow: ellipsis; } + padding-bottom: 4px; + text-overflow: ellipsis; + text-transform: uppercase; } .card-outer .card-title { - margin: 0 0 2px; font-size: 14px; - word-wrap: break-word; - line-height: 19px; } + line-height: 19px; + margin: 0 0 2px; + word-wrap: break-word; } .card-outer .card-description { font-size: 12px; + line-height: 19px; margin: 0; - word-wrap: break-word; overflow: hidden; - line-height: 19px; } + word-wrap: break-word; } .card-outer .card-context { + bottom: 0; + color: #737373; + display: flex; + font-size: 11px; + left: 0; padding: 12px 16px 12px 14px; position: absolute; - bottom: 0; - left: 0; - right: 0; - color: #737373; - font-size: 11px; - display: flex; } + right: 0; } .card-outer .card-context-icon { fill: rgba(12, 12, 13, 0.6); margin-inline-end: 6px; } @@ -1103,13 +1103,13 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { display: none; } @media (min-width: 544px) { .manual-migration-container .icon { + align-self: center; display: block; fill: rgba(12, 12, 13, 0.6); - margin-inline-end: 6px; - align-self: center; } } + margin-inline-end: 6px; } } .manual-migration-actions { - border: none; + border: 0; display: block; flex-wrap: nowrap; } @media (min-width: 544px) { @@ -1131,8 +1131,8 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .collapsible-section .section-title .icon-arrowhead-down, .collapsible-section .section-title .icon-arrowhead-forward { - margin-top: -1px; - margin-inline-start: 8px; } + margin-inline-start: 8px; + margin-top: -1px; } .collapsible-section .section-top-bar { position: relative; } @@ -1142,30 +1142,36 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { top: 0; } .collapsible-section .section-top-bar .info-option-icon { background-image: url("../data/content/assets/glyph-info-option-12.svg"); - background-size: 12px 12px; - background-repeat: no-repeat; background-position: center; - fill: rgba(12, 12, 13, 0.6); + background-repeat: no-repeat; + background-size: 12px 12px; -moz-context-properties: fill; - height: 16px; - width: 16px; display: inline-block; + fill: rgba(12, 12, 13, 0.6); + height: 16px; margin-bottom: -2px; opacity: 0; - transition: opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); } - .collapsible-section .section-top-bar .info-option-icon:focus, .collapsible-section .section-top-bar .info-option-icon:active { + transition: opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); + width: 16px; } + .collapsible-section .section-top-bar .info-option-icon[aria-expanded='true'] { + background-color: rgba(12, 12, 13, 0.1); + border-radius: 1px; + box-shadow: 0 0 0 5px rgba(12, 12, 13, 0.1); + fill: rgba(12, 12, 13, 0.8); } + .collapsible-section .section-top-bar .info-option-icon[aria-expanded='true'] + .info-option { + opacity: 1; + transition: visibility 0.2s, opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); + visibility: visible; } + .collapsible-section .section-top-bar .info-option-icon:not([aria-expanded='true']) + .info-option { + pointer-events: none; } + .collapsible-section .section-top-bar .info-option-icon:-moz-any(:active, :focus) { opacity: 1; } - .collapsible-section .section-top-bar .info-option-icon[aria-expanded="true"] { - background-color: rgba(12, 12, 13, 0.1); - border-radius: 1px; - box-shadow: 0 0 0 5px rgba(12, 12, 13, 0.1); - fill: rgba(12, 12, 13, 0.8); } .collapsible-section .section-top-bar .section-info-option .info-option { - visibility: hidden; opacity: 0; - transition: visibility 0.2s, opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); } + transition: visibility 0.2s, opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); + visibility: hidden; } .collapsible-section .section-top-bar .section-info-option .info-option::after, .collapsible-section .section-top-bar .section-info-option .info-option::before { - content: ""; + content: ''; offset-inline-end: 0; position: absolute; } .collapsible-section .section-top-bar .section-info-option .info-option::before { @@ -1182,27 +1188,21 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { height: 10px; offset-inline-start: 0; top: -10px; } - .collapsible-section .section-top-bar .info-option-icon[aria-expanded="true"] + .info-option { - visibility: visible; - opacity: 1; - transition: visibility 0.2s, opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); } - .collapsible-section .section-top-bar .info-option-icon:not([aria-expanded="true"]) + .info-option { - pointer-events: none; } .collapsible-section .section-top-bar .info-option { - z-index: 9999; - position: absolute; background: #FFF; border: 1px solid #D7D7DB; border-radius: 3px; + box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); font-size: 13px; line-height: 120%; margin-inline-end: -9px; offset-inline-end: 0; - top: 26px; - width: 320px; padding: 24px; - box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); - -moz-user-select: none; } + position: absolute; + top: 26px; + -moz-user-select: none; + width: 320px; + z-index: 9999; } .collapsible-section .section-top-bar .info-option-header { font-size: 15px; font-weight: 600; } @@ -1215,8 +1215,8 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .collapsible-section .section-top-bar .info-option-manage { margin-top: 24px; } .collapsible-section .section-top-bar .info-option-manage button { - background: none; - border: none; + background: 0; + border: 0; color: #0060DF; cursor: pointer; margin: 0; @@ -1256,14 +1256,14 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { color: #008EA4; padding-left: 3px; } .collapsible-section .section-disclaimer button { - margin-top: 2px; - offset-inline-end: 0; - min-height: 26px; - max-width: 130px; background: #F9F9FA; border: 1px solid #B1B1B3; border-radius: 4px; - cursor: pointer; } + cursor: pointer; + margin-top: 2px; + max-width: 130px; + min-height: 26px; + offset-inline-end: 0; } .collapsible-section .section-disclaimer button:hover:not(.dismiss) { box-shadow: 0 0 0 5px #D7D7DB; transition: box-shadow 150ms; } diff --git a/browser/extensions/activity-stream/css/activity-stream-windows.css b/browser/extensions/activity-stream/css/activity-stream-windows.css index 0d7ef2799ef6..04a04888c215 100644 --- a/browser/extensions/activity-stream/css/activity-stream-windows.css +++ b/browser/extensions/activity-stream/css/activity-stream-windows.css @@ -23,15 +23,15 @@ input { display: none !important; } .icon { - display: inline-block; - width: 16px; - height: 16px; - background-size: 16px; background-position: center center; background-repeat: no-repeat; - vertical-align: middle; + background-size: 16px; + -moz-context-properties: fill; + display: inline-block; fill: rgba(12, 12, 13, 0.8); - -moz-context-properties: fill; } + height: 16px; + vertical-align: middle; + width: 16px; } .icon.icon-spacer { margin-inline-end: 8px; } .icon.icon-small-spacer { @@ -44,9 +44,9 @@ input { background-image: url("../data/content/assets/glyph-delete-16.svg"); } .icon.icon-modal-delete { background-image: url("../data/content/assets/glyph-modal-delete-32.svg"); - width: 32px; + background-size: 32px; height: 32px; - background-size: 32px; } + width: 32px; } .icon.icon-dismiss { background-image: url("../data/content/assets/glyph-dismiss-16.svg"); } .icon.icon-info { @@ -71,8 +71,7 @@ input { background-image: url("../data/content/assets/glyph-historyItem-16.svg"); } .icon.icon-trending { background-image: url("../data/content/assets/glyph-trending-16.svg"); - transform: translateY(2px); - /* trending bolt is visually top heavy */ } + transform: translateY(2px); } .icon.icon-now { background-image: url("chrome://browser/skin/history.svg"); } .icon.icon-topsites { @@ -125,24 +124,24 @@ a { color: #008EA4; } .sr-only { - position: absolute; - width: 1px; + border: 0; + clip: rect(0, 0, 0, 0); height: 1px; - padding: 0; margin: -1px; overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; } + padding: 0; + position: absolute; + width: 1px; } .inner-border { border: 1px solid #D7D7DB; border-radius: 3px; + height: 100%; + left: 0; + pointer-events: none; position: absolute; top: 0; - left: 0; width: 100%; - height: 100%; - pointer-events: none; z-index: 100; } @keyframes fadeIn { @@ -155,25 +154,25 @@ a { opacity: 0; transition: opacity 0.2s ease-in; } .show-on-init.on { - opacity: 1; - animation: fadeIn 0.2s; } + animation: fadeIn 0.2s; + opacity: 1; } .actions { border-top: 1px solid #D7D7DB; display: flex; flex-direction: row; - margin: 0; - padding: 15px 25px 0 25px; + flex-wrap: wrap; justify-content: flex-start; - flex-wrap: wrap; } + margin: 0; + padding: 15px 25px 0; } .actions button { background-color: #F9F9FA; border: 1px solid #B1B1B3; border-radius: 4px; color: inherit; cursor: pointer; - padding: 10px 30px; margin-bottom: 15px; + padding: 10px 30px; white-space: nowrap; } .actions button:hover:not(.dismiss) { box-shadow: 0 0 0 5px #D7D7DB; @@ -193,16 +192,16 @@ a { .outer-wrapper { display: flex; - padding: 40px 32px 32px; + flex-grow: 1; height: 100%; - flex-grow: 1; } + padding: 40px 32px 32px; } .outer-wrapper.fixed-to-top { height: auto; } main { margin: auto; - width: 224px; - padding-bottom: 48px; } + padding-bottom: 48px; + width: 224px; } @media (min-width: 416px) { main { width: 352px; } } @@ -245,49 +244,50 @@ main { list-style: none; margin: 0; margin-bottom: -18px; - padding: 0; - margin-inline-end: -32px; } + margin-inline-end: -32px; + padding: 0; } @media (max-width: 416px) { .top-sites-list :nth-child(2n+1) .context-menu { - margin-inline-start: auto; margin-inline-end: auto; - offset-inline-start: -32px; - offset-inline-end: auto; } + margin-inline-start: auto; + offset-inline-end: auto; + offset-inline-start: -32px; } .top-sites-list :nth-child(2n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 416px) and (max-width: 544px) { - .top-sites-list :nth-child(3n+2) .context-menu, .top-sites-list :nth-child(3n) .context-menu { - margin-inline-start: auto; + .top-sites-list :nth-child(3n+2) .context-menu, + .top-sites-list :nth-child(3n) .context-menu { margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 544px) and (max-width: 800px) { .top-sites-list :nth-child(4n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 544px) and (max-width: 768px) { .top-sites-list :nth-child(4n+3) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 800px) and (max-width: 1248px) { .top-sites-list :nth-child(6n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 800px) and (max-width: 1024px) { .top-sites-list :nth-child(6n+5) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } .top-sites-list li { display: inline-block; margin: 0 0 8px; @@ -295,53 +295,56 @@ main { .top-sites-list .top-site-outer { position: relative; } .top-sites-list .top-site-outer > a { - display: block; color: inherit; + display: block; outline: none; } - .top-sites-list .top-site-outer > a.active .tile, .top-sites-list .top-site-outer > a:focus .tile { + .top-sites-list .top-site-outer > a:-moz-any(.active, :focus) .tile { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px #D7D7DB; transition: box-shadow 150ms; } .top-sites-list .top-site-outer .context-menu-button { - cursor: pointer; - position: absolute; - top: -13.5px; - offset-inline-end: -13.5px; - width: 27px; - height: 27px; + background-clip: padding-box; background-color: #FFF; background-image: url("chrome://browser/skin/page-action.svg"); background-position: 55%; - background-clip: padding-box; border: 1px solid #B1B1B3; border-radius: 100%; box-shadow: 0 2px rgba(12, 12, 13, 0.1); + cursor: pointer; fill: rgba(12, 12, 13, 0.8); - transform: scale(0.25); + height: 27px; + offset-inline-end: -13.5px; opacity: 0; + position: absolute; + top: -13.5px; + transform: scale(0.25); + transition-duration: 200ms; transition-property: transform, opacity; - transition-duration: 200ms; } - .top-sites-list .top-site-outer .context-menu-button:focus, .top-sites-list .top-site-outer .context-menu-button:active { - transform: scale(1); - opacity: 1; } - .top-sites-list .top-site-outer:hover .tile, .top-sites-list .top-site-outer:focus .tile, .top-sites-list .top-site-outer.active .tile { + width: 27px; } + .top-sites-list .top-site-outer .context-menu-button:-moz-any(:active, :focus) { + opacity: 1; + transform: scale(1); } + .top-sites-list .top-site-outer:-moz-any(.active, :focus, :hover) .tile { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px #D7D7DB; transition: box-shadow 150ms; } - .top-sites-list .top-site-outer:hover .context-menu-button, .top-sites-list .top-site-outer:focus .context-menu-button, .top-sites-list .top-site-outer.active .context-menu-button { - transform: scale(1); - opacity: 1; } + .top-sites-list .top-site-outer:-moz-any(.active, :focus, :hover) .edit-menu { + opacity: 1; + transform: scale(1); } + .top-sites-list .top-site-outer:-moz-any(.active, :focus, :hover) .context-menu-button { + opacity: 1; + transform: scale(1); } .top-sites-list .top-site-outer .tile { - position: relative; - height: 96px; - width: 96px; border-radius: 6px; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 1px 4px 0 rgba(12, 12, 13, 0.1); - color: #737373; - font-weight: 200; - font-size: 32px; - text-transform: uppercase; - display: flex; + height: 96px; + position: relative; + width: 96px; align-items: center; - justify-content: center; } + color: #737373; + display: flex; + font-size: 32px; + font-weight: 200; + justify-content: center; + text-transform: uppercase; } .top-sites-list .top-site-outer .tile::before { content: attr(data-fallback); } .top-sites-list .top-site-outer.placeholder .tile { @@ -349,43 +352,43 @@ main { .top-sites-list .top-site-outer.placeholder .screenshot { display: none; } .top-sites-list .top-site-outer .screenshot { - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; background-color: #FFF; + background-position: top left; + background-size: cover; border-radius: 6px; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); - background-size: cover; - background-position: top left; + height: 100%; + left: 0; + opacity: 0; + position: absolute; + top: 0; transition: opacity 1s; - opacity: 0; } + width: 100%; } .top-sites-list .top-site-outer .screenshot.active { opacity: 1; } .top-sites-list .top-site-outer .top-site-icon { - position: absolute; - border-radius: 6px; - box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); + background-color: #F9F9FA; background-position: center center; background-repeat: no-repeat; - background-color: #F9F9FA; } + border-radius: 6px; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); + position: absolute; } .top-sites-list .top-site-outer .rich-icon { - top: 0; - offset-inline-start: 0; + background-size: 96px; height: 100%; - width: 100%; - background-size: 96px; } + offset-inline-start: 0; + top: 0; + width: 100%; } .top-sites-list .top-site-outer .default-icon { + background-size: 32px; bottom: -6px; height: 42px; offset-inline-end: -6px; width: 42px; - background-size: 32px; - display: flex; align-items: center; - justify-content: center; - font-size: 20px; } + display: flex; + font-size: 20px; + justify-content: center; } .top-sites-list .top-site-outer .default-icon[data-fallback]::before { content: attr(data-fallback); } .top-sites-list .top-site-outer .title { @@ -423,7 +426,7 @@ main { transition-property: transform, opacity; transition-duration: 200ms; z-index: 1000; } - .top-sites-list .top-site-outer .edit-menu:focus, .top-sites-list .top-site-outer .edit-menu:active { + .top-sites-list .top-site-outer .edit-menu:-moz-any(:active, :focus) { transform: scale(1); opacity: 1; } .top-sites-list .top-site-outer .edit-menu button { @@ -443,9 +446,6 @@ main { border-right: 0; } .top-sites-list .top-site-outer .edit-menu button:first-child:dir(rtl) { border-right: 0; } - .top-sites-list .top-site-outer:hover .edit-menu, .top-sites-list .top-site-outer:focus .edit-menu, .top-sites-list .top-site-outer.active .edit-menu { - transform: scale(1); - opacity: 1; } .edit-topsites-wrapper .edit-topsites-button { border-right: 1px solid #D7D7DB; @@ -460,7 +460,7 @@ main { .edit-topsites-wrapper .edit-topsites-button:dir(rtl) { border-left: 1px solid #D7D7DB; border-right: 0; } - .edit-topsites-wrapper .edit-topsites-button:focus, .edit-topsites-wrapper .edit-topsites-button:active { + .edit-topsites-wrapper .edit-topsites-button:-moz-any(:active, :focus) { opacity: 1; } .edit-topsites-wrapper .edit-topsites-button button { background: none; @@ -499,7 +499,7 @@ main { .edit-topsites-wrapper .show-less span { padding-inline-start: 3px; } -section.top-sites:not(.collapsed):hover .edit-topsites-button { +.top-sites:not(.collapsed):hover .edit-topsites-button { opacity: 1; pointer-events: auto; } @@ -564,59 +564,59 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { transform: translateY(0); } } .sections-list .section-list { - margin: 0; display: grid; + grid-gap: 32px; grid-template-columns: repeat(auto-fit, 224px); - grid-gap: 32px; } + margin: 0; } @media (max-width: 544px) { .sections-list .section-list .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 544px) and (max-width: 800px) { .sections-list .section-list :nth-child(2n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } @media (min-width: 800px) and (max-width: 1248px) { .sections-list .section-list :nth-child(3n) .context-menu { - margin-inline-start: auto; margin-inline-end: 5px; - offset-inline-start: auto; - offset-inline-end: 0; } } + margin-inline-start: auto; + offset-inline-end: 0; + offset-inline-start: auto; } } .sections-list .section-empty-state { - width: 100%; - height: 266px; - display: flex; border: 1px solid #D7D7DB; - border-radius: 3px; } + border-radius: 3px; + display: flex; + height: 266px; + width: 100%; } .sections-list .section-empty-state .empty-state { margin: auto; max-width: 350px; } .sections-list .section-empty-state .empty-state .empty-state-icon { - background-size: 50px 50px; - background-repeat: no-repeat; background-position: center; - fill: rgba(12, 12, 13, 0.6); + background-repeat: no-repeat; + background-size: 50px 50px; -moz-context-properties: fill; + display: block; + fill: rgba(12, 12, 13, 0.6); height: 50px; - width: 50px; margin: 0 auto; - display: block; } + width: 50px; } .sections-list .section-empty-state .empty-state .empty-state-message { - margin-bottom: 0; - font-size: 13px; color: #737373; + font-size: 13px; + margin-bottom: 0; text-align: center; } .topic { - font-size: 12px; color: #737373; - margin-top: 12px; - line-height: 1.6; } + font-size: 12px; + line-height: 1.6; + margin-top: 12px; } @media (min-width: 800px) { .topic { line-height: 16px; } } @@ -656,27 +656,27 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .topic .topic-read-more:dir(rtl)::after { transform: scaleX(-1); } .topic::after { - content: ""; - display: table; - clear: both; } + clear: both; + content: ''; + display: table; } .search-wrapper { cursor: default; display: flex; - position: relative; + height: 35px; margin: 1px 1px 40px; - width: 100%; - height: 35px; } + position: relative; + width: 100%; } .search-wrapper input { - border: none; + border: 0; border-radius: 3px; box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.15); color: inherit; + font-size: 15px; padding: 0; padding-inline-end: 36px; padding-inline-start: 35px; - width: 100%; - font-size: 15px; } + width: 100%; } .search-wrapper:hover input { box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.25); } .search-wrapper:active input, @@ -684,23 +684,23 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { box-shadow: 0 0 0 1px #0A84FF; } .search-wrapper .search-label { background: url("chrome://browser/skin/search-glass.svg") no-repeat 12px center/16px; - fill: rgba(12, 12, 13, 0.4); -moz-context-properties: fill; - position: absolute; - offset-inline-start: 0; + fill: rgba(12, 12, 13, 0.4); height: 100%; + offset-inline-start: 0; + position: absolute; width: 35px; } .search-wrapper .search-button { background: url("chrome://browser/skin/forward.svg") no-repeat center center; - border-radius: 0 3px 3px 0; - border: 0; - width: 36px; - fill: rgba(12, 12, 13, 0.4); - -moz-context-properties: fill; background-size: 16px 16px; + border: 0; + border-radius: 0 3px 3px 0; + -moz-context-properties: fill; + fill: rgba(12, 12, 13, 0.4); height: 100%; offset-inline-end: 0; - position: absolute; } + position: absolute; + width: 36px; } .search-wrapper .search-button:focus, .search-wrapper .search-button:hover { background-color: rgba(12, 12, 13, 0.1); cursor: pointer; } @@ -713,43 +713,43 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { transform: translateY(2px); } .context-menu { - display: block; - position: absolute; - font-size: 14px; - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 0, 0, 0.2); - top: 6.75px; - offset-inline-start: 100%; - margin-inline-start: 5px; - z-index: 10000; background: #F9F9FA; - border-radius: 5px; } + border-radius: 5px; + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 0, 0, 0.2); + display: block; + font-size: 14px; + margin-inline-start: 5px; + offset-inline-start: 100%; + position: absolute; + top: 6.75px; + z-index: 10000; } .context-menu > ul { + list-style: none; margin: 0; - padding: 5px 0; - list-style: none; } + padding: 5px 0; } .context-menu > ul > li { margin: 0; width: 100%; } .context-menu > ul > li.separator { - margin: 5px 0; - border-bottom: 1px solid rgba(0, 0, 0, 0.2); } + border-bottom: 1px solid rgba(0, 0, 0, 0.2); + margin: 5px 0; } .context-menu > ul > li > a { - outline: none; - cursor: pointer; + align-items: center; color: inherit; - white-space: nowrap; - padding: 3px 12px; - line-height: 16px; + cursor: pointer; display: flex; - align-items: center; } - .context-menu > ul > li > a:hover, .context-menu > ul > li > a:focus { + line-height: 16px; + outline: none; + padding: 3px 12px; + white-space: nowrap; } + .context-menu > ul > li > a:-moz-any(:focus, :hover) { background: #0060DF; color: #FFF; } - .context-menu > ul > li > a:hover a, .context-menu > ul > li > a:focus a { + .context-menu > ul > li > a:-moz-any(:focus, :hover) a { color: #0C0C0D; } - .context-menu > ul > li > a:hover .icon, .context-menu > ul > li > a:focus .icon { + .context-menu > ul > li > a:-moz-any(:focus, :hover) .icon { fill: #FFF; } - .context-menu > ul > li > a:hover:hover, .context-menu > ul > li > a:hover:focus, .context-menu > ul > li > a:focus:hover, .context-menu > ul > li > a:focus:focus { + .context-menu > ul > li > a:-moz-any(:focus, :hover):-moz-any(:focus, :hover) { color: #FFF; } .prefs-pane { @@ -859,14 +859,14 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .prefs-pane [type='checkbox']:checked + label::after { background: url("chrome://global/skin/in-content/check.svg") no-repeat center center; content: ''; + -moz-context-properties: fill, stroke; + fill: #0060DF; height: 21px; offset-inline-start: 0; position: absolute; + stroke: none; top: 0; - width: 21px; - -moz-context-properties: fill, stroke; - fill: #0060DF; - stroke: none; } + width: 21px; } .prefs-pane [type='checkbox']:not(:checked) + label::after { opacity: 0; } .prefs-pane [type='checkbox']:checked + label::after { @@ -882,9 +882,9 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { border: 0; cursor: pointer; fill: rgba(12, 12, 13, 0.6); + offset-inline-end: 15px; padding: 15px; position: fixed; - offset-inline-end: 15px; top: 15px; z-index: 12001; } .prefs-pane-button button:hover { @@ -893,35 +893,35 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { background-color: #F9F9FA; } .confirmation-dialog .modal { - position: fixed; - width: 400px; - top: 20%; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1); left: 50%; margin-left: -200px; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.08); } + position: fixed; + top: 20%; + width: 400px; } .confirmation-dialog section { margin: 0; } .confirmation-dialog .modal-message { + display: flex; padding: 16px; - padding-bottom: 0; - display: flex; } + padding-bottom: 0; } .confirmation-dialog .modal-message p { margin: 0; margin-bottom: 16px; } .confirmation-dialog .actions { - padding: 0px 16px 0 16px; - border: none; + border: 0; + display: flex; flex-wrap: nowrap; - display: flex; } + padding: 0 16px; } .confirmation-dialog .actions button { margin-inline-end: 16px; width: 50%; } .confirmation-dialog .actions button.done { - margin-inline-start: 0; - margin-inline-end: 0; } + margin-inline-end: 0; + margin-inline-start: 0; } .confirmation-dialog .icon { margin-inline-end: 16px; } @@ -945,83 +945,83 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .card-outer { background: #FFF; - display: inline-block; - margin-inline-end: 32px; - width: 224px; border-radius: 3px; + display: inline-block; height: 266px; - position: relative; } + margin-inline-end: 32px; + position: relative; + width: 224px; } .card-outer .context-menu-button { - cursor: pointer; - position: absolute; - top: -13.5px; - offset-inline-end: -13.5px; - width: 27px; - height: 27px; + background-clip: padding-box; background-color: #FFF; background-image: url("chrome://browser/skin/page-action.svg"); background-position: 55%; - background-clip: padding-box; border: 1px solid #B1B1B3; border-radius: 100%; box-shadow: 0 2px rgba(12, 12, 13, 0.1); + cursor: pointer; fill: rgba(12, 12, 13, 0.8); - transform: scale(0.25); + height: 27px; + offset-inline-end: -13.5px; opacity: 0; + position: absolute; + top: -13.5px; + transform: scale(0.25); + transition-duration: 200ms; transition-property: transform, opacity; - transition-duration: 200ms; } - .card-outer .context-menu-button:focus, .card-outer .context-menu-button:active { - transform: scale(1); - opacity: 1; } + width: 27px; } + .card-outer .context-menu-button:-moz-any(:active, :focus) { + opacity: 1; + transform: scale(1); } .card-outer.placeholder { background: transparent; } .card-outer.placeholder .card { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); } .card-outer .card { - height: 100%; border-radius: 3px; - box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); } + box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); + height: 100%; } .card-outer > a { - display: block; color: inherit; + display: block; height: 100%; outline: none; position: absolute; width: 224px; } - .card-outer > a.active .card, .card-outer > a:focus .card { + .card-outer > a:-moz-any(.active, :focus) .card { box-shadow: 0 0 0 5px #D7D7DB; transition: box-shadow 150ms; } - .card-outer > a.active .card-title, .card-outer > a:focus .card-title { + .card-outer > a:-moz-any(.active, :focus) .card-title { color: #0060DF; } .card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) { - outline: none; box-shadow: 0 0 0 5px #D7D7DB; - transition: box-shadow 150ms; } + transition: box-shadow 150ms; + outline: none; } .card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) .context-menu-button { - transform: scale(1); - opacity: 1; } + opacity: 1; + transform: scale(1); } .card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) .card-title { color: #0060DF; } .card-outer .card-preview-image-outer { background-color: #F9F9FA; - position: relative; - height: 122px; border-radius: 3px 3px 0 0; - overflow: hidden; } + height: 122px; + overflow: hidden; + position: relative; } .card-outer .card-preview-image-outer::after { border-bottom: 1px solid rgba(0, 0, 0, 0.05); bottom: 0; - content: " "; + content: ''; position: absolute; width: 100%; } .card-outer .card-preview-image-outer .card-preview-image { - width: 100%; - height: 100%; - background-size: cover; background-position: center; background-repeat: no-repeat; + background-size: cover; + height: 100%; opacity: 0; - transition: opacity 1s cubic-bezier(0.07, 0.95, 0, 1); } + transition: opacity 1s cubic-bezier(0.07, 0.95, 0, 1); + width: 100%; } .card-outer .card-preview-image-outer .card-preview-image.loaded { opacity: 1; } .card-outer .card-details { @@ -1029,8 +1029,8 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .card-outer .card-details.no-image { padding-top: 16px; } .card-outer .card-text { - overflow: hidden; - max-height: 78px; } + max-height: 78px; + overflow: hidden; } .card-outer .card-text.no-image { max-height: 192px; } .card-outer .card-text.no-host-name, .card-outer .card-text.no-context { @@ -1047,30 +1047,30 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .card-outer .card-host-name { color: #737373; font-size: 10px; - padding-bottom: 4px; - text-transform: uppercase; overflow: hidden; - text-overflow: ellipsis; } + padding-bottom: 4px; + text-overflow: ellipsis; + text-transform: uppercase; } .card-outer .card-title { - margin: 0 0 2px; font-size: 14px; - word-wrap: break-word; - line-height: 19px; } + line-height: 19px; + margin: 0 0 2px; + word-wrap: break-word; } .card-outer .card-description { font-size: 12px; + line-height: 19px; margin: 0; - word-wrap: break-word; overflow: hidden; - line-height: 19px; } + word-wrap: break-word; } .card-outer .card-context { + bottom: 0; + color: #737373; + display: flex; + font-size: 11px; + left: 0; padding: 12px 16px 12px 14px; position: absolute; - bottom: 0; - left: 0; - right: 0; - color: #737373; - font-size: 11px; - display: flex; } + right: 0; } .card-outer .card-context-icon { fill: rgba(12, 12, 13, 0.6); margin-inline-end: 6px; } @@ -1103,13 +1103,13 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { display: none; } @media (min-width: 544px) { .manual-migration-container .icon { + align-self: center; display: block; fill: rgba(12, 12, 13, 0.6); - margin-inline-end: 6px; - align-self: center; } } + margin-inline-end: 6px; } } .manual-migration-actions { - border: none; + border: 0; display: block; flex-wrap: nowrap; } @media (min-width: 544px) { @@ -1131,8 +1131,8 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .collapsible-section .section-title .icon-arrowhead-down, .collapsible-section .section-title .icon-arrowhead-forward { - margin-top: -1px; - margin-inline-start: 8px; } + margin-inline-start: 8px; + margin-top: -1px; } .collapsible-section .section-top-bar { position: relative; } @@ -1142,30 +1142,36 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { top: 0; } .collapsible-section .section-top-bar .info-option-icon { background-image: url("../data/content/assets/glyph-info-option-12.svg"); - background-size: 12px 12px; - background-repeat: no-repeat; background-position: center; - fill: rgba(12, 12, 13, 0.6); + background-repeat: no-repeat; + background-size: 12px 12px; -moz-context-properties: fill; - height: 16px; - width: 16px; display: inline-block; + fill: rgba(12, 12, 13, 0.6); + height: 16px; margin-bottom: -2px; opacity: 0; - transition: opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); } - .collapsible-section .section-top-bar .info-option-icon:focus, .collapsible-section .section-top-bar .info-option-icon:active { + transition: opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); + width: 16px; } + .collapsible-section .section-top-bar .info-option-icon[aria-expanded='true'] { + background-color: rgba(12, 12, 13, 0.1); + border-radius: 1px; + box-shadow: 0 0 0 5px rgba(12, 12, 13, 0.1); + fill: rgba(12, 12, 13, 0.8); } + .collapsible-section .section-top-bar .info-option-icon[aria-expanded='true'] + .info-option { + opacity: 1; + transition: visibility 0.2s, opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); + visibility: visible; } + .collapsible-section .section-top-bar .info-option-icon:not([aria-expanded='true']) + .info-option { + pointer-events: none; } + .collapsible-section .section-top-bar .info-option-icon:-moz-any(:active, :focus) { opacity: 1; } - .collapsible-section .section-top-bar .info-option-icon[aria-expanded="true"] { - background-color: rgba(12, 12, 13, 0.1); - border-radius: 1px; - box-shadow: 0 0 0 5px rgba(12, 12, 13, 0.1); - fill: rgba(12, 12, 13, 0.8); } .collapsible-section .section-top-bar .section-info-option .info-option { - visibility: hidden; opacity: 0; - transition: visibility 0.2s, opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); } + transition: visibility 0.2s, opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); + visibility: hidden; } .collapsible-section .section-top-bar .section-info-option .info-option::after, .collapsible-section .section-top-bar .section-info-option .info-option::before { - content: ""; + content: ''; offset-inline-end: 0; position: absolute; } .collapsible-section .section-top-bar .section-info-option .info-option::before { @@ -1182,27 +1188,21 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { height: 10px; offset-inline-start: 0; top: -10px; } - .collapsible-section .section-top-bar .info-option-icon[aria-expanded="true"] + .info-option { - visibility: visible; - opacity: 1; - transition: visibility 0.2s, opacity 0.2s cubic-bezier(0.07, 0.95, 0, 1); } - .collapsible-section .section-top-bar .info-option-icon:not([aria-expanded="true"]) + .info-option { - pointer-events: none; } .collapsible-section .section-top-bar .info-option { - z-index: 9999; - position: absolute; background: #FFF; border: 1px solid #D7D7DB; border-radius: 3px; + box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); font-size: 13px; line-height: 120%; margin-inline-end: -9px; offset-inline-end: 0; - top: 26px; - width: 320px; padding: 24px; - box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); - -moz-user-select: none; } + position: absolute; + top: 26px; + -moz-user-select: none; + width: 320px; + z-index: 9999; } .collapsible-section .section-top-bar .info-option-header { font-size: 15px; font-weight: 600; } @@ -1215,8 +1215,8 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { .collapsible-section .section-top-bar .info-option-manage { margin-top: 24px; } .collapsible-section .section-top-bar .info-option-manage button { - background: none; - border: none; + background: 0; + border: 0; color: #0060DF; cursor: pointer; margin: 0; @@ -1256,14 +1256,14 @@ section.top-sites:not(.collapsed):hover .edit-topsites-button { color: #008EA4; padding-left: 3px; } .collapsible-section .section-disclaimer button { - margin-top: 2px; - offset-inline-end: 0; - min-height: 26px; - max-width: 130px; background: #F9F9FA; border: 1px solid #B1B1B3; border-radius: 4px; - cursor: pointer; } + cursor: pointer; + margin-top: 2px; + max-width: 130px; + min-height: 26px; + offset-inline-end: 0; } .collapsible-section .section-disclaimer button:hover:not(.dismiss) { box-shadow: 0 0 0 5px #D7D7DB; transition: box-shadow 150ms; } diff --git a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js index 2922c6ea1eb1..14ec5eb5457a 100644 --- a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js +++ b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js @@ -94,7 +94,7 @@ const globalImportContext = typeof Window === "undefined" ? BACKGROUND_PROCESS : // UNINIT: "UNINIT" // } const actionTypes = {}; -for (const type of ["BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_HISTORY_URL", "DELETE_HISTORY_URL_CONFIRM", "DIALOG_CANCEL", "DIALOG_OPEN", "DISABLE_ONBOARDING", "INIT", "MIGRATION_CANCEL", "MIGRATION_COMPLETED", "MIGRATION_START", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_REHYDRATED", "NEW_TAB_STATE_REQUEST", "NEW_TAB_UNLOAD", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PAGE_PRERENDERED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_DELETED", "PLACES_LINK_BLOCKED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "RICH_ICON_MISSING", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SETTINGS_CLOSE", "SETTINGS_OPEN", "SET_PREF", "SHOW_FIREFOX_ACCOUNTS", "SNIPPETS_DATA", "SNIPPETS_RESET", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_ADD", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_EDIT", "TOP_SITES_PIN", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "UNINIT"]) { +for (const type of ["BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_HISTORY_URL", "DELETE_HISTORY_URL_CONFIRM", "DIALOG_CANCEL", "DIALOG_OPEN", "DISABLE_ONBOARDING", "INIT", "MIGRATION_CANCEL", "MIGRATION_COMPLETED", "MIGRATION_START", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_REHYDRATED", "NEW_TAB_STATE_REQUEST", "NEW_TAB_UNLOAD", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PAGE_PRERENDERED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_DELETED", "PLACES_LINK_BLOCKED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "RICH_ICON_MISSING", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SETTINGS_CLOSE", "SETTINGS_OPEN", "SET_PREF", "SHOW_FIREFOX_ACCOUNTS", "SNIPPETS_BLOCKLIST_UPDATED", "SNIPPETS_DATA", "SNIPPETS_RESET", "SNIPPET_BLOCKED", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_ADD", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_EDIT", "TOP_SITES_PIN", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "UNINIT"]) { actionTypes[type] = type; } @@ -310,27 +310,27 @@ module.exports = ReactRedux; /* 4 */ /***/ (function(module, exports) { -var g; - -// This works in non-strict mode -g = (function() { - return this; -})(); - -try { - // This works if eval is allowed (see CSP) - g = g || Function("return this")() || (1,eval)("this"); -} catch(e) { - // This works if the window reference is available - if(typeof window === "object") - g = window; -} - -// g can still be undefined, but nothing to do about it... -// We return undefined, instead of nothing here, so it's -// easier to handle this case. if(!global) { ...} - -module.exports = g; +var g; + +// This works in non-strict mode +g = (function() { + return this; +})(); + +try { + // This works if eval is allowed (see CSP) + g = g || Function("return this")() || (1,eval)("this"); +} catch(e) { + // This works if the window reference is available + if(typeof window === "object") + g = window; +} + +// g can still be undefined, but nothing to do about it... +// We return undefined, instead of nothing here, so it's +// easier to handle this case. if(!global) { ...} + +module.exports = g; /***/ }), @@ -2473,11 +2473,6 @@ class Search extends React.PureComponent { // In the future, when activity stream is default about:home, this can be renamed window.gContentSearchController = new ContentSearchUIController(input, input.parentNode, healthReportKey, searchSource); addEventListener("ContentSearchClient", this); - - // Focus the search box if we are on about:home - if (!IS_NEWTAB) { - input.focus(); - } } else { window.gContentSearchController = null; removeEventListener("ContentSearchClient", this); @@ -3769,8 +3764,9 @@ class SnippetsMap extends Map { let blockList = this.blockList; if (!blockList.includes(id)) { blockList.push(id); + this._dispatch(ac.SendToMain({ type: at.SNIPPETS_BLOCKLIST_UPDATED, data: blockList })); + await this.set("blockList", blockList); } - await this.set("blockList", blockList); } disableOnboarding() { @@ -3893,6 +3889,7 @@ class SnippetsProvider { // Initialize the Snippets Map and attaches it to a global so that // the snippet payload can interact with it. global.gSnippetsMap = new SnippetsMap(dispatch); + this._onAction = this._onAction.bind(this); } get snippetsMap() { @@ -3958,6 +3955,7 @@ class SnippetsProvider { } // Note that injecting snippets can throw if they're invalid XML. + // eslint-disable-next-line no-unsanitized/property snippetsEl.innerHTML = payload; // Scripts injected by innerHTML are inactive, so we have to relocate them @@ -3969,6 +3967,13 @@ class SnippetsProvider { } } + _onAction(msg) { + if (msg.data.type === at.SNIPPET_BLOCKED) { + this.snippetsMap.set("blockList", msg.data.data); + document.getElementById("snippets-container").style.display = "none"; + } + } + /** * init - Fetch the snippet payload and show snippets * @@ -3985,6 +3990,11 @@ class SnippetsProvider { connect: true }, options); + // Add listener so we know when snippets are blocked on other pages + if (global.addMessageListener) { + global.addMessageListener("ActivityStream:MainToContent", this._onAction); + } + // TODO: Requires enabling indexedDB on newtab // Restore the snippets map from indexedDB if (this.connect) { @@ -4019,6 +4029,9 @@ class SnippetsProvider { uninit() { window.dispatchEvent(new Event(SNIPPETS_DISABLED_EVENT)); this._forceOnboardingVisibility(false); + if (global.removeMessageListener) { + global.removeMessageListener("ActivityStream:MainToContent", this._onAction); + } this.initialized = false; } } diff --git a/browser/extensions/activity-stream/install.rdf.in b/browser/extensions/activity-stream/install.rdf.in index 0c93c864ae51..e03b98cb8c22 100644 --- a/browser/extensions/activity-stream/install.rdf.in +++ b/browser/extensions/activity-stream/install.rdf.in @@ -8,7 +8,7 @@ 2 true false - 2017.11.16.1254-39442ee8 + 2017.12.02.0024-b0532674 Activity Stream A rich visual history feed and a reimagined home page make it easier than ever to find exactly what you're looking for in Firefox. true diff --git a/browser/extensions/activity-stream/lib/ActivityStream.jsm b/browser/extensions/activity-stream/lib/ActivityStream.jsm index 1de3bee7220a..bcb6bf11293d 100644 --- a/browser/extensions/activity-stream/lib/ActivityStream.jsm +++ b/browser/extensions/activity-stream/lib/ActivityStream.jsm @@ -41,10 +41,6 @@ const REASON_ADDON_UNINSTALL = 6; // Configure default Activity Stream prefs with a plain `value` or a `getValue` // that computes a value. A `value_local_dev` is used for development defaults. const PREFS_CONFIG = new Map([ - ["aboutHome.autoFocus", { - title: "Focus the about:home search box on load", - value: false - }], ["default.sites", { title: "Comma-separated list of default top sites to fill in behind visited sites", getValue: ({geo}) => DEFAULT_SITES.get(DEFAULT_SITES.has(geo) ? geo : "") diff --git a/browser/extensions/activity-stream/lib/NewTabInit.jsm b/browser/extensions/activity-stream/lib/NewTabInit.jsm index 7e5d811b23b3..ba54105c699a 100644 --- a/browser/extensions/activity-stream/lib/NewTabInit.jsm +++ b/browser/extensions/activity-stream/lib/NewTabInit.jsm @@ -40,13 +40,6 @@ this.NewTabInit = class NewTabInit { if (action.data.simulated) { this._repliedEarlyTabs.set(action.data.portID, false); } - - if (action.data.url === "about:home") { - const prefs = this.store.getState().Prefs.values; - if (prefs["aboutHome.autoFocus"] && prefs.showSearch) { - action.data.browser.focus(); - } - } break; case at.NEW_TAB_UNLOAD: // Clean up for any tab (no-op if not an early tab) diff --git a/browser/extensions/activity-stream/lib/SnippetsFeed.jsm b/browser/extensions/activity-stream/lib/SnippetsFeed.jsm index f1ceb43d1100..01ebb3185e56 100644 --- a/browser/extensions/activity-stream/lib/SnippetsFeed.jsm +++ b/browser/extensions/activity-stream/lib/SnippetsFeed.jsm @@ -143,6 +143,9 @@ this.SnippetsFeed = class SnippetsFeed { case at.SHOW_FIREFOX_ACCOUNTS: this.showFirefoxAccounts(action._target.browser); break; + case at.SNIPPETS_BLOCKLIST_UPDATED: + this.store.dispatch(ac.BroadcastToContent({type: at.SNIPPET_BLOCKED, data: action.data})); + break; } } }; diff --git a/browser/extensions/activity-stream/prerendered/locales/ast/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ast/activity-stream-prerendered.html index 5061d3a4c2db..f37338c9d97b 100644 --- a/browser/extensions/activity-stream/prerendered/locales/ast/activity-stream-prerendered.html +++ b/browser/extensions/activity-stream/prerendered/locales/ast/activity-stream-prerendered.html @@ -8,7 +8,7 @@ -

Sitios destacaos

Recommended by Pocket

Temes populares:

    Los destacaos

    +

    Más visitaos

    Recomendáu por Pocket

    Temes populares:

      Destacaos

      diff --git a/browser/extensions/activity-stream/prerendered/locales/ast/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/ast/activity-stream-strings.js index c6c64b841228..23c15c855b05 100644 --- a/browser/extensions/activity-stream/prerendered/locales/ast/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/ast/activity-stream-strings.js @@ -2,12 +2,12 @@ window.gActivityStreamStrings = { "newtab_page_title": "Llingüeta nueva", "default_label_loading": "Cargando…", - "header_top_sites": "Sitios destacaos", + "header_top_sites": "Más visitaos", "header_stories": "Histories destacaes", - "header_highlights": "Los destacaos", + "header_highlights": "Destacaos", "header_visit_again": "Visitar de nueves", "header_bookmarks": "Marcadores recientes", - "header_recommended_by": "Recommended by {provider}", + "header_recommended_by": "Recomendáu por {provider}", "header_bookmarks_placeholder": "Entá nun tienes dengún marcador.", "header_stories_from": "de", "type_label_visited": "Visitóse", @@ -39,60 +39,60 @@ window.gActivityStreamStrings = { "section_info_send_feedback": "Unviar comentarios", "section_info_privacy_notice": "Nota de privacidá", "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", - "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories_linktext": "Deprendi cómo furrula.", + "section_disclaimer_topstories_buttontext": "Val, píllolo", "welcome_title": "Afáyate na llingüeta nueva", "welcome_body": "Firefox usará esti espaciu p'amosate los marcadores, artículos, vídeos y páxines más relevantes que visitares apocayá, asina pues volver a ellos de mou cenciellu.", - "welcome_label": "Identifying your Highlights", + "welcome_label": "Identificando los tos destacaos", "time_label_less_than_minute": "<1m", "time_label_minute": "{number}m", "time_label_hour": "{number}h", "time_label_day": "{number}d", - "settings_pane_button_label": "Customize your New Tab page", - "settings_pane_header": "New Tab Preferences", - "settings_pane_body2": "Choose what you see on this page.", + "settings_pane_button_label": "Personalizar páxina Llingüeta nueva", + "settings_pane_header": "Preferencies de Llingüeta nueva", + "settings_pane_body2": "Escueyi qué quies ver nesta páxina", "settings_pane_search_header": "Search", - "settings_pane_search_body": "Search the Web from your new tab.", - "settings_pane_topsites_header": "Top Sites", - "settings_pane_topsites_body": "Access the websites you visit most.", - "settings_pane_topsites_options_showmore": "Show two rows", - "settings_pane_bookmarks_header": "Recent Bookmarks", - "settings_pane_bookmarks_body": "Your newly created bookmarks in one handy location.", - "settings_pane_visit_again_header": "Visit Again", - "settings_pane_visit_again_body": "Firefox will show you parts of your browsing history that you might want to remember or get back to.", - "settings_pane_highlights_header": "Highlights", - "settings_pane_highlights_body2": "Find your way back to interesting things you’ve recently visited or bookmarked.", - "settings_pane_highlights_options_bookmarks": "Bookmarks", - "settings_pane_highlights_options_visited": "Visited Sites", - "settings_pane_snippets_header": "Snippets", - "settings_pane_snippets_body": "Read short and sweet updates from Mozilla about Firefox, internet culture, and the occasional random meme.", + "settings_pane_search_body": "Restolar na Web dende la nueva llingüeta", + "settings_pane_topsites_header": "Más visitaos", + "settings_pane_topsites_body": "Acceder a les webs que más visites.", + "settings_pane_topsites_options_showmore": "Amosar dos fileres", + "settings_pane_bookmarks_header": "Marcadores recientes", + "settings_pane_bookmarks_body": "Los marcadores recién fechos, nun llugar accesible.", + "settings_pane_visit_again_header": "Visitar de nueves", + "settings_pane_visit_again_body": "Firefox va amosate partes del to historial de navegación que a lo meyor prestaríate remembrar o volver visitar.", + "settings_pane_highlights_header": "Destacaos", + "settings_pane_highlights_body2": "Atopa otra vegada les coses interesantes que yá visitaras o marcaras.", + "settings_pane_highlights_options_bookmarks": "Marcadores", + "settings_pane_highlights_options_visited": "Sitios visitaos", + "settings_pane_snippets_header": "Retayos", + "settings_pane_snippets_body": "Llei anovamientos curtios de Mozilla tocante a Firefox, la cultura d'internet y un meme de xemes en cuandu.", "settings_pane_done_button": "Fecho", - "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", - "edit_topsites_button_text": "Edit", - "edit_topsites_button_label": "Customize your Top Sites section", + "settings_pane_topstories_options_sponsored": "Amosar hestories patrocinaes", + "edit_topsites_button_text": "Editar", + "edit_topsites_button_label": "Personalizar la seición de Más visitaos", "edit_topsites_showmore_button": "Amosar más", - "edit_topsites_showless_button": "Show Fewer", + "edit_topsites_showless_button": "Amosar menos", "edit_topsites_done_button": "Fecho", - "edit_topsites_pin_button": "Pin this site", - "edit_topsites_unpin_button": "Unpin this site", - "edit_topsites_edit_button": "Edit this site", - "edit_topsites_dismiss_button": "Dismiss this site", - "edit_topsites_add_button": "Add", - "topsites_form_add_header": "New Top Site", - "topsites_form_edit_header": "Edit Top Site", - "topsites_form_title_placeholder": "Enter a title", - "topsites_form_url_placeholder": "Type or paste a URL", + "edit_topsites_pin_button": "Fixar esti sitiu", + "edit_topsites_unpin_button": "Desfixar esti sitiu", + "edit_topsites_edit_button": "Editar esti sitiu", + "edit_topsites_dismiss_button": "Escartar esti sitiu", + "edit_topsites_add_button": "Amestar", + "topsites_form_add_header": "Nuevu Sitiu más visitáu", + "topsites_form_edit_header": "Editar Sitiu más visitáu", + "topsites_form_title_placeholder": "Introducir títulu", + "topsites_form_url_placeholder": "Escribi o apega una URL", "topsites_form_add_button": "Amestar", "topsites_form_save_button": "Guardar", "topsites_form_cancel_button": "Encaboxar", - "topsites_form_url_validation": "Valid URL required", + "topsites_form_url_validation": "Ríquese una URL válida", "pocket_read_more": "Temes populares:", "pocket_read_even_more": "Ver más histories", "pocket_feedback_header": "The best of the web, curated by over 25 million people.", "pocket_description": "Discover high-quality content you might otherwise miss, with help from Pocket, now part of Mozilla.", "highlights_empty_state": "Start browsing, and we’ll show some of the great articles, videos, and other pages you’ve recently visited or bookmarked here.", "topstories_empty_state": "You’ve caught up. Check back later for more top stories from {provider}. Can’t wait? Select a popular topic to find more great stories from around the web.", - "manual_migration_explanation2": "Try Firefox with the bookmarks, history and passwords from another browser.", - "manual_migration_cancel_button": "No Thanks", - "manual_migration_import_button": "Import Now" + "manual_migration_explanation2": "Prueba Firefox colos marcadores, hestorial y contraseñes d'otru restolador.", + "manual_migration_cancel_button": "Non, gracies", + "manual_migration_import_button": "Importar agora" }; diff --git a/browser/extensions/activity-stream/prerendered/locales/be/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/be/activity-stream-strings.js index 3fb2758837ab..616b6f31fed0 100644 --- a/browser/extensions/activity-stream/prerendered/locales/be/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/be/activity-stream-strings.js @@ -38,9 +38,9 @@ window.gActivityStreamStrings = { "section_info_option": "Звесткі", "section_info_send_feedback": "Даслаць водгук", "section_info_privacy_notice": "Паведамленне аб прыватнасці", - "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", - "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories": "Самыя цікавыя гісторыі з інтэрнэту на аснове таго, што вы чытаеце. Падборка ад Pocket, які цяпер частка Mozilla.", + "section_disclaimer_topstories_linktext": "Даведайцеся, як гэта працуе.", + "section_disclaimer_topstories_buttontext": "Зразумела", "welcome_title": "Калі ласка ў новую картку", "welcome_body": "Firefox будзе выкарыстоўваць гэта месца, каб адлюстроўваць самыя актуальныя закладкі, артыкулы, відэа і старонкі, якія вы нядаўна наведалі, каб вы змаглі лёгка трапіць на іх зноў.", "welcome_label": "Вызначэнне вашага выбранага", @@ -67,7 +67,7 @@ window.gActivityStreamStrings = { "settings_pane_snippets_header": "Урыўкі", "settings_pane_snippets_body": "Чытайце кароткія і радасныя навіны ад Mozilla аб Firefox, інтэрнэт-культуру і выпадковыя мемы.", "settings_pane_done_button": "Гатова", - "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", + "settings_pane_topstories_options_sponsored": "Паказаць артыкулы ад спонсараў", "edit_topsites_button_text": "Правіць", "edit_topsites_button_label": "Наладзіць раздзел папулярных сайтаў", "edit_topsites_showmore_button": "Паказаць больш", diff --git a/browser/extensions/activity-stream/prerendered/locales/bn-BD/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/bn-BD/activity-stream-strings.js index 7b87a2d5cdb0..6d4ec2e2727c 100644 --- a/browser/extensions/activity-stream/prerendered/locales/bn-BD/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/bn-BD/activity-stream-strings.js @@ -38,9 +38,9 @@ window.gActivityStreamStrings = { "section_info_option": "তথ্য", "section_info_send_feedback": "মতামত পাঠান", "section_info_privacy_notice": "গোপনীয়তা বিজ্ঞপ্তি", - "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", - "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories": "মজার মজার সব গল্প নির্বাচিত হয়েছে, আপনি যেমনটা পড়েন। Pocket এখন থেকে Mozilla এর অংশ।", + "section_disclaimer_topstories_linktext": "কিভাবে কাজ করে জানুন।", + "section_disclaimer_topstories_buttontext": "ঠিক আছে, বুঝেছি", "welcome_title": "নতুন ট্যাবে আপনাকে স্বাগতম", "welcome_body": "আপনার সাথে মিলে এমন বুর্কমার্ক, নিবন্ধ, ভিডিও এবং পাতা যেগুলো আপনি সম্প্রতি ভ্রমণ করেছে তা Firefox এই জায়গায় দেখাবে, যাতে আপনি সেগুলো দ্রুত খুঁজে পান।", "welcome_label": "আপনার হাইলাইট সমূহ চিহ্নিত করুন", @@ -67,7 +67,7 @@ window.gActivityStreamStrings = { "settings_pane_snippets_header": "টুকিটাকি", "settings_pane_snippets_body": "Mozilla থেকে Firefox, ইন্টারনেট সংস্কৃতি, এবং মাঝে মাঝে উদ্দেশ্যহীন মেমে সম্পর্কে ছোট এবং মিষ্টি আপডেটগুলি পড়ুন।", "settings_pane_done_button": "হয়েছে", - "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", + "settings_pane_topstories_options_sponsored": "বিজ্ঞাপনী গল্প দেখাও", "edit_topsites_button_text": "সম্পাদনা", "edit_topsites_button_label": "আপনার টপ সাইট সেকশন কাস্টমাইজ করুন", "edit_topsites_showmore_button": "আরও দেখান", diff --git a/browser/extensions/activity-stream/prerendered/locales/bn-IN/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/bn-IN/activity-stream-strings.js index 5707c1becb82..fd83b9a9c75f 100644 --- a/browser/extensions/activity-stream/prerendered/locales/bn-IN/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/bn-IN/activity-stream-strings.js @@ -38,9 +38,9 @@ window.gActivityStreamStrings = { "section_info_option": "তথ্য", "section_info_send_feedback": "মতামত পাঠান", "section_info_privacy_notice": "গোপনীয়তা বিজ্ঞপ্তি", - "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", - "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories": "মজার মজার সব গল্প নির্বাচিত হয়েছে, আপনি যেমনটা পড়েন। Pocket এখন থেকে Mozilla এর অংশ।", + "section_disclaimer_topstories_linktext": "কিভাবে কাজ করে জানুন।", + "section_disclaimer_topstories_buttontext": "ঠিক আছে, বুঝেছি", "welcome_title": "নতুন ট্যাবে স্বাগতম", "welcome_body": "আপনার সাথে মিলে এমন বুর্কমার্ক, নিবন্ধ, ভিডিও এবং পাতা যেগুলো আপনি সম্প্রতি ভ্রমণ করেছে তা Firefox এই জায়গায় দেখাবে, যাতে আপনি সেগুলো দ্রুত খুঁজে পান।", "welcome_label": "আপনার হাইলাইট সমূহ চিহ্নিত করা হচ্ছে", @@ -67,7 +67,7 @@ window.gActivityStreamStrings = { "settings_pane_snippets_header": "টুকিটাকি", "settings_pane_snippets_body": "Mozilla থেকে Firefox, ইন্টারনেট সংস্কৃতি, এবং মাঝে মাঝে উদ্দেশ্যহীন মেমে সম্পর্কে ছোট এবং মিষ্টি আপডেটগুলি পড়ুন।", "settings_pane_done_button": "হয়েছে", - "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", + "settings_pane_topstories_options_sponsored": "বিজ্ঞাপনী গল্প দেখাও", "edit_topsites_button_text": "সম্পাদনা", "edit_topsites_button_label": "আপনার শীর্ষ সাইট সেকশন কাস্টমাইজ করুন", "edit_topsites_showmore_button": "আরও দেখান", diff --git a/browser/extensions/activity-stream/prerendered/locales/es-AR/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/es-AR/activity-stream-strings.js index f0a78f75cbbb..d842f4e0d6c2 100644 --- a/browser/extensions/activity-stream/prerendered/locales/es-AR/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/es-AR/activity-stream-strings.js @@ -38,9 +38,9 @@ window.gActivityStreamStrings = { "section_info_option": "Información", "section_info_send_feedback": "Enviar opinión", "section_info_privacy_notice": "Nota de privacidad", - "section_disclaimer_topstories": "Las más interesantes historias en la web, seleccionadas basándonos en los que lees. Desde Pocket, ahora parte de Mozilla.", - "section_disclaimer_topstories_linktext": "Saber como trabaja.", - "section_disclaimer_topstories_buttontext": "Está bien, lo entiendo", + "section_disclaimer_topstories": "Las historias más interesantes en la web, seleccionadas en base a lo que lees. Gracias a Pocket, ahora parte de Mozilla.", + "section_disclaimer_topstories_linktext": "Aprendé cómo funciona.", + "section_disclaimer_topstories_buttontext": "Listo, lo entendí", "welcome_title": "Bienvenido a una nueva pestaña", "welcome_body": "Firefox usará este espacio para mostrar sus marcadores, artículos, videos y páginas más relevantes que se hayan visitado para poder volver más fácilmente.", "welcome_label": "Identificar los destacados", diff --git a/browser/extensions/activity-stream/prerendered/locales/et/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/et/activity-stream-strings.js index 6b3cf4458837..8ebfc9254838 100644 --- a/browser/extensions/activity-stream/prerendered/locales/et/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/et/activity-stream-strings.js @@ -39,8 +39,8 @@ window.gActivityStreamStrings = { "section_info_send_feedback": "Saada tagasisidet", "section_info_privacy_notice": "Privaatsusreeglid", "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", - "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories_linktext": "Vaata, kuidas see töötab.", + "section_disclaimer_topstories_buttontext": "Olgu, sain aru", "welcome_title": "Tere tulemast uuele kaardile", "welcome_body": "Firefox kasutab seda lehte, et kuvada sulle kõige olulisemaid järjehoidjaid, artikleid, videoid ja lehti, mida oled hiljuti külastanud, nii et pääseksid kergelt nende juurde tagasi.", "welcome_label": "Esiletõstetava sisu tuvastamine", diff --git a/browser/extensions/activity-stream/prerendered/locales/fi/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/fi/activity-stream-strings.js index 9655ea67da64..b4466959ba41 100644 --- a/browser/extensions/activity-stream/prerendered/locales/fi/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/fi/activity-stream-strings.js @@ -38,9 +38,9 @@ window.gActivityStreamStrings = { "section_info_option": "Tietoa", "section_info_send_feedback": "Anna palautetta", "section_info_privacy_notice": "Tietosuojakäytäntö", - "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", - "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories": "Verkon kiinnostavimmat jutut, lukemasi perusteella valittuna. Pocketilta, joka on nyt osa Mozillaa.", + "section_disclaimer_topstories_linktext": "Lue, miten tämä toimii.", + "section_disclaimer_topstories_buttontext": "Selvä", "welcome_title": "Tervetuloa uuteen välilehteen", "welcome_body": "Firefox käyttää tätä tilaa näyttämään olennaisimmat kirjanmerkit, artikkelit, videot ja sivut, joita olet katsellut, jotta pääset niihin takaisin nopeasti.", "welcome_label": "Tunnistetaan nostojasi", @@ -64,10 +64,10 @@ window.gActivityStreamStrings = { "settings_pane_highlights_body2": "Löydä tiesi takaisin kiinnostaviin juttuihin, joissa olet käynyt tai jotka olet lisännyt kirjanmerkkeihin viime aikoina.", "settings_pane_highlights_options_bookmarks": "Kirjanmerkit", "settings_pane_highlights_options_visited": "Vieraillut sivustot", - "settings_pane_snippets_header": "Snippets", - "settings_pane_snippets_body": "Read short and sweet updates from Mozilla about Firefox, internet culture, and the occasional random meme.", + "settings_pane_snippets_header": "Tiedonmuruset", + "settings_pane_snippets_body": "Lue Mozillan lyhyitä päivityksiä liittyen Firefoxiin, internetkulttuuriin ja satunnaisiin meemeihin.", "settings_pane_done_button": "Valmis", - "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", + "settings_pane_topstories_options_sponsored": "Näytä sponsoroidut jutut", "edit_topsites_button_text": "Muokkaa", "edit_topsites_button_label": "Muokkaa Ykkössivustot-osiota", "edit_topsites_showmore_button": "Näytä enemmän", @@ -89,10 +89,10 @@ window.gActivityStreamStrings = { "pocket_read_more": "Suositut aiheet:", "pocket_read_even_more": "Katso lisää juttuja", "pocket_feedback_header": "Netin parhaat palat, valikoitu yli 25 miljoonan ihmisen voimin.", - "pocket_description": "Discover high-quality content you might otherwise miss, with help from Pocket, now part of Mozilla.", - "highlights_empty_state": "Start browsing, and we’ll show some of the great articles, videos, and other pages you’ve recently visited or bookmarked here.", + "pocket_description": "Löydä laadukasta sisältöä, josta olisit muutoin ehkä jäänyt paitsi. Pocketilta, joka on nyt osa Mozillaa.", + "highlights_empty_state": "Ala selata, niin tässä alkaa näkyä hyviä juttuja, videoita ja muita sivuja, joilla olet käynyt hiljattain tai jotka olet lisännyt kirjanmerkkeihin.", "topstories_empty_state": "Ei enempää suosituksia juuri nyt. Katso myöhemmin uudestaan lisää ykkösjuttuja lähteestä {provider}. Etkö malta odottaa? Valitse suosittu aihe ja löydä lisää hyviä juttuja ympäri verkkoa.", - "manual_migration_explanation2": "Try Firefox with the bookmarks, history and passwords from another browser.", + "manual_migration_explanation2": "Kokeile Firefoxia toisesta selaimesta tuotujen kirjanmerkkien, historian ja salasanojen kanssa.", "manual_migration_cancel_button": "Ei kiitos", "manual_migration_import_button": "Tuo nyt" }; diff --git a/browser/extensions/activity-stream/prerendered/locales/fy-NL/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/fy-NL/activity-stream-strings.js index f398d09d7d90..bfc0ac959d4f 100644 --- a/browser/extensions/activity-stream/prerendered/locales/fy-NL/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/fy-NL/activity-stream-strings.js @@ -38,9 +38,9 @@ window.gActivityStreamStrings = { "section_info_option": "Ynfo", "section_info_send_feedback": "Kommentaar ferstjoere", "section_info_privacy_notice": "Privacyferklearring", - "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", - "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories": "De meast ynteressante ferhalen op it web, selektearre op basis fan wat jo lêzen hawwe. Fan Pocket, no ûnderdiel fan Mozilla.", + "section_disclaimer_topstories_linktext": "Lês hoe't it wurket.", + "section_disclaimer_topstories_buttontext": "Oké, begrepen", "welcome_title": "Wolkom by it nije ljepblêd", "welcome_body": "Firefox brûkt dizze romte om jo meast relevante blêdwizers, artikelen, fideo’s en siden dy't jo koartlyn besocht hawwe wer te jaan, sadat jo dizze ienfâldichwei weromfine kinne.", "welcome_label": "Jo hichtepunten oantsjutte", @@ -67,7 +67,7 @@ window.gActivityStreamStrings = { "settings_pane_snippets_header": "Koarte ynformaasje", "settings_pane_snippets_body": "Lês koart nijs fan Mozilla oer Firefox, ynternetkultuer en somtiden in meme.", "settings_pane_done_button": "Dien", - "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", + "settings_pane_topstories_options_sponsored": "Sponsore ferhalen toane", "edit_topsites_button_text": "Bewurkje", "edit_topsites_button_label": "Jo seksje Topwebsites oanpasse", "edit_topsites_showmore_button": "Mear toane", diff --git a/browser/extensions/activity-stream/prerendered/locales/gu-IN/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/gu-IN/activity-stream-strings.js index cddf8ea1e3b4..0ddc6db992d1 100644 --- a/browser/extensions/activity-stream/prerendered/locales/gu-IN/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/gu-IN/activity-stream-strings.js @@ -38,9 +38,9 @@ window.gActivityStreamStrings = { "section_info_option": "માહિતી", "section_info_send_feedback": "પ્રતિસાદ મોકલ", "section_info_privacy_notice": "ગોપનીયતા સૂચના", - "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", - "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories": "વેબ પરની સૌથી રસપ્રદ વાર્તાઓ, તમે જે વાંચો છો તેના આધારે પસંદ કરેલ છે. Pocket થી, હવે Mozilla નો ભાગ.", + "section_disclaimer_topstories_linktext": "તે કેવી રીતે કાર્ય કરે છે તે જાણો.", + "section_disclaimer_topstories_buttontext": "ઠીક છે, સમજાઇ ગયું", "welcome_title": "નવી વિન્ડોમાં આપનું સ્વાગત છે", "welcome_body": "ફાયરફોક્સ, તમારા સૌથી સંબંધિત બુકમાર્ક્સ, લેખો, વિડિઓઝ, અને પૃષ્ઠો જે તમે તાજેતરમાં મુલાકાત લીધી એ બતાવવા માટે આ જગ્યાનો ઉપયોગ કરશે જેથી તમે પાછા તેમને સરળતાથી મેળવી શકો છો.", "welcome_label": "તમારા હાઇલાઇટ્સ ઓળખવા", @@ -67,7 +67,7 @@ window.gActivityStreamStrings = { "settings_pane_snippets_header": "જાણકારી આપનારા ઉતારા ક કાપલીઓ", "settings_pane_snippets_body": "ટૂંકી અને મીઠી સુધારાઓ વાંચો મોઝિલ્લાથી ફાયરફોક્સ વિશે, ઇન્ટરનેટ સંસ્કૃતિ અને પ્રસંગોપાત ફાવે તેમ મેમે વિશે.", "settings_pane_done_button": "પૂરું", - "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", + "settings_pane_topstories_options_sponsored": "પ્રાયોજિત વાર્તાઓ બતાવો", "edit_topsites_button_text": "ફેરફાર કરો", "edit_topsites_button_label": "તમારા ટોચના સાઇટ્સ વિભાગને કસ્ટમાઇઝ કરો", "edit_topsites_showmore_button": "વધારે બતાવો", diff --git a/browser/extensions/activity-stream/prerendered/locales/it/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/it/activity-stream-strings.js index 5ade5229486e..4a76c3d7ce30 100644 --- a/browser/extensions/activity-stream/prerendered/locales/it/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/it/activity-stream-strings.js @@ -38,9 +38,9 @@ window.gActivityStreamStrings = { "section_info_option": "Info", "section_info_send_feedback": "Invia feedback", "section_info_privacy_notice": "Informativa sulla privacy", - "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", - "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories": "Le storie più interessanti del Web, selezionate in base alle tue letture. Direttamente da Pocket, ora parte del gruppo Mozilla.", + "section_disclaimer_topstories_linktext": "Scopri come funziona.", + "section_disclaimer_topstories_buttontext": "Ho capito.", "welcome_title": "Benvenuto nella nuova scheda", "welcome_body": "Firefox utilizzerà questo spazio per visualizzare gli elementi più significativi, come segnalibri, articoli, video e pagine visitate di recente, in modo che siano sempre facili da raggiungere.", "welcome_label": "Identificazione elementi in evidenza…", @@ -67,7 +67,7 @@ window.gActivityStreamStrings = { "settings_pane_snippets_header": "Snippet", "settings_pane_snippets_body": "Brevi notizie direttamente da Mozilla a proposito di Firefox, Internet, senza dimenticare qualche meme di tanto in tanto.", "settings_pane_done_button": "Fatto", - "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", + "settings_pane_topstories_options_sponsored": "Visualizza articoli sponsorizzati", "edit_topsites_button_text": "Modifica", "edit_topsites_button_label": "Personalizza la sezione Siti principali", "edit_topsites_showmore_button": "Visualizza altri", diff --git a/browser/extensions/activity-stream/prerendered/locales/ka/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/ka/activity-stream-strings.js index d7707fb47e4c..5f718adb1fb2 100644 --- a/browser/extensions/activity-stream/prerendered/locales/ka/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/ka/activity-stream-strings.js @@ -8,7 +8,7 @@ window.gActivityStreamStrings = { "header_visit_again": "ხელახლა ნახვა", "header_bookmarks": "ბოლოს ჩანიშნულები", "header_recommended_by": "რეკომენდებულია {provider}-ის მიერ", - "header_bookmarks_placeholder": "სანიშნეები ჯერ არაა დამატებული.", + "header_bookmarks_placeholder": "სანიშნები ჯერ არაა დამატებული.", "header_stories_from": "-იდან", "type_label_visited": "მონახულებული", "type_label_bookmarked": "ჩანიშნული", @@ -18,12 +18,12 @@ window.gActivityStreamStrings = { "type_label_topic": "თემა", "type_label_now": "ახლა", "menu_action_bookmark": "ჩანიშვნა", - "menu_action_remove_bookmark": "სანიშნეებიდან ამოღება", + "menu_action_remove_bookmark": "სანიშნებიდან ამოღება", "menu_action_copy_address": "მისამართის დაკოპირება", "menu_action_email_link": "ბმულის გაგზავნა…", "menu_action_open_new_window": "ახალ ფანჯარაში გახსნა", "menu_action_open_private_window": "ახალ პირად ფანჯარაში გახსნა", - "menu_action_dismiss": "დახურვა", + "menu_action_dismiss": "დამალვა", "menu_action_delete": "ისტორიიდან ამოშლა", "menu_action_pin": "მიმაგრება", "menu_action_unpin": "მოხსნა", @@ -42,7 +42,7 @@ window.gActivityStreamStrings = { "section_disclaimer_topstories_linktext": "ნახეთ, როგორ მუშაობს.", "section_disclaimer_topstories_buttontext": "კარგი, გასაგებია", "welcome_title": "მოგესალმებით ახალ ჩანართზე", - "welcome_body": "Firefox ამ სივრცეს გამოიყენებს თქვენთვის ყველაზე საჭირო სანიშნეების, სტატიების, ვიდეოებისა და ბოლოს მონახულებული გვერდებისთვის, რომ ადვილად შეძლოთ მათზე დაბრუნება.", + "welcome_body": "Firefox ამ სივრცეს გამოიყენებს თქვენთვის ყველაზე საჭირო სანიშნების, სტატიების, ვიდეოებისა და ბოლოს მონახულებული გვერდებისთვის, რომ ადვილად შეძლოთ მათზე დაბრუნება.", "welcome_label": "მნიშვნელოვანი საიტების დადგენა", "time_label_less_than_minute": "<1წთ", "time_label_minute": "{number}წთ", @@ -57,12 +57,12 @@ window.gActivityStreamStrings = { "settings_pane_topsites_body": "წვდომა ხშირად მონახულებულ საიტებთან.", "settings_pane_topsites_options_showmore": "ორ რიგად ჩვენება", "settings_pane_bookmarks_header": "ბოლოს ჩანიშნულები", - "settings_pane_bookmarks_body": "ახლად შექმნილი სანიშნეები, ერთი ხელის გაწვდენაზე.", + "settings_pane_bookmarks_body": "ახლად შექმნილი სანიშნები, ერთი ხელის გაწვდენაზე.", "settings_pane_visit_again_header": "ხელახლა ნახვა", "settings_pane_visit_again_body": "Firefox გაჩვენებთ მონახულებული გვერდების ისტორიიდან იმას, რისი გახსენებაც ან რაზე დაბრუნებაც გენდომებათ.", "settings_pane_highlights_header": "მნიშვნელოვანი საიტები", "settings_pane_highlights_body2": "მარტივად დაუბრუნდით ბოლოს მონახულებულ, ან ჩანიშნულ გვერდებს.", - "settings_pane_highlights_options_bookmarks": "სანიშნეები", + "settings_pane_highlights_options_bookmarks": "სანიშნები", "settings_pane_highlights_options_visited": "მონახულებული საიტები", "settings_pane_snippets_header": "ცნობები", "settings_pane_snippets_body": "გაეცანით მოკლე, საინტერესო სიახლეებს Mozilla-სგან, Firefox-ის, ინტერნეტ სამყაროს მიღწევებისა და სხვა დასამახსოვრებელი ფაქტების შესახებ.", @@ -92,7 +92,7 @@ window.gActivityStreamStrings = { "pocket_description": "გაეცანით ინტერნეტში არსებულ მაღალი ხარისხის მასალას Pocket-ის საშუალებით, რომელიც ახლა უკვე Mozilla-ს ნაწილს წარმოადგენს.", "highlights_empty_state": "დაიწყეთ გვერდების დათვალიერება და აქ გამოჩნდება თქვენი რჩეული სტატიები, ვიდეოები და ბოლოს მონახულებული, ან ჩანიშნული საიტები.", "topstories_empty_state": "უკვე ყველაფერი წაკითხული გაქვთ. {provider}-იდან ახალი რჩეული სტატიების მისაღებად, მოგვიანებით შემოიარეთ. თუ ვერ ითმენთ, აირჩიეთ რომელიმე მოთხოვნადი თემა, ახალი საინტერესო სტატიების მოსაძიებლად.", - "manual_migration_explanation2": "გადმოიტანეთ სხვა ბრაუზერებიდან თქვენი სანიშნეები, ისტორია და პაროლები Firefox-ში.", + "manual_migration_explanation2": "გადმოიტანეთ სხვა ბრაუზერებიდან თქვენი სანიშნები, ისტორია და პაროლები Firefox-ში.", "manual_migration_cancel_button": "არა, გმადლობთ", "manual_migration_import_button": "ახლავე გადმოტანა" }; diff --git a/browser/extensions/activity-stream/prerendered/locales/km/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/km/activity-stream-prerendered.html index 43f05edab8d7..fb7e24c75211 100644 --- a/browser/extensions/activity-stream/prerendered/locales/km/activity-stream-prerendered.html +++ b/browser/extensions/activity-stream/prerendered/locales/km/activity-stream-prerendered.html @@ -8,7 +8,7 @@ -

      វិបសាយ​លើ​គេ

      Recommended by Pocket

      Popular Topics:

        ការ​រំលេច

        +

        វិបសាយ​លើ​គេ

        បានណែនាំដោយ Pocket

        ប្រធានបទកំពុងពេញនិយម៖

          រឿងសំខាន់ៗ

          diff --git a/browser/extensions/activity-stream/prerendered/locales/km/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/km/activity-stream-strings.js index c05e1aabeb7e..ad65750c7bc6 100644 --- a/browser/extensions/activity-stream/prerendered/locales/km/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/km/activity-stream-strings.js @@ -3,20 +3,20 @@ window.gActivityStreamStrings = { "newtab_page_title": "ផ្ទាំង​ថ្មី", "default_label_loading": "កំពុង​ផ្ទុក...", "header_top_sites": "វិបសាយ​លើ​គេ", - "header_stories": "Top Stories", - "header_highlights": "ការ​រំលេច", - "header_visit_again": "Visit Again", - "header_bookmarks": "Recent Bookmarks", - "header_recommended_by": "Recommended by {provider}", - "header_bookmarks_placeholder": "You don’t have any bookmarks yet.", - "header_stories_from": "from", + "header_stories": "រឿងរ៉ាវកំពូល", + "header_highlights": "រឿងសំខាន់ៗ", + "header_visit_again": "ទស្សនាម្តងទៀត", + "header_bookmarks": "ចំណាំថ្មីៗ", + "header_recommended_by": "បានណែនាំដោយ {provider}", + "header_bookmarks_placeholder": "អ្នកមិនមានចំណាំណាមួយនៅឡើយទេ ។", + "header_stories_from": "មកពី", "type_label_visited": "បាន​ចូល​មើល", "type_label_bookmarked": "បាន​ចំណាំ", "type_label_synced": "បាន​ធ្វើ​សមកាលកម្ម​ពី​ឧបករណ៍​ផ្សេង​ទៀត", - "type_label_recommended": "Trending", + "type_label_recommended": "និន្នាការ", "type_label_open": "បើក", "type_label_topic": "ប្រធានបទ", - "type_label_now": "Now", + "type_label_now": "ឥឡូវនេះ", "menu_action_bookmark": "ចំណាំ", "menu_action_remove_bookmark": "លុប​ចំណាំ​ចេញ", "menu_action_copy_address": "ចម្លង​អាសយដ្ឋាន", @@ -25,22 +25,22 @@ window.gActivityStreamStrings = { "menu_action_open_private_window": "បើក​នៅ​ក្នុង​បង្អួច​ឯកជន​ថ្មី", "menu_action_dismiss": "បោះបង់ចោល", "menu_action_delete": "លុប​ពី​ប្រវត្តិ", - "menu_action_pin": "Pin", - "menu_action_unpin": "Unpin", - "confirm_history_delete_p1": "Are you sure you want to delete every instance of this page from your history?", - "confirm_history_delete_notice_p2": "This action cannot be undone.", - "menu_action_save_to_pocket": "Save to Pocket", + "menu_action_pin": "ខ្ទាស់", + "menu_action_unpin": "ដកខ្ទាស់", + "confirm_history_delete_p1": "តើអ្នកប្រាកដថាអ្នកចង់លុបគ្រប់វត្ថុនៃទំព័រនេះពីប្រវត្តិរបស់អ្នកឬ?", + "confirm_history_delete_notice_p2": "សកម្មភាពនេះមិនអាចមិនធ្វើឡើង​វិញបានទេ។", + "menu_action_save_to_pocket": "រក្សាទុកទៅ Pocket", "search_for_something_with": "ស្វែងរក {search_term} ជាមួយ៖", - "search_button": "Search", + "search_button": "ស្វែងរក", "search_header": "{search_engine_name} ស្វែងរក", "search_web_placeholder": "ស្វែងរក​បណ្ដាញ", "search_settings": "ផ្លាស់ប្ដូរ​ការ​កំណត់​ស្វែងរក", - "section_info_option": "Info", - "section_info_send_feedback": "Send Feedback", - "section_info_privacy_notice": "Privacy Notice", + "section_info_option": "ព័ត៌មាន", + "section_info_send_feedback": "ផ្ញើមតិត្រឡប់", + "section_info_privacy_notice": "សេចក្តីជូនដំណឹងអំពីភាពឯកជន", "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories_buttontext": "យល់​ហើយ", "welcome_title": "ស្វាគមន៍​មក​កាន់​ផ្ទាំង​ថ្មី", "welcome_body": "Firefox នឹង​ប្រើប្រាស់​កន្លែង​ទំនេរ​នេះ ដើម្បី​បង្ហាញ​ចំណាំ អត្ថបទ វីដេអូ និង​ទំព័រ​ដែល​ទាក់ទង​អ្នក​បំផុត ដែល​អ្នក​បាន​ចូល​មើល​ថ្មីៗ​នេះ ដូច្នេះ​អ្នក​អាច​ត្រឡប់​ទៅ​​កាន់​​វា​​វិញ​បាន​យ៉ាងងាយស្រួល។", "welcome_label": "កំពុង​បញ្ជាក់​ការ​រំលេច​របស់​អ្នក", @@ -49,50 +49,50 @@ window.gActivityStreamStrings = { "time_label_hour": "{number} ម៉ោង", "time_label_day": "{number} ថ្ងៃ", "settings_pane_button_label": "Customize your New Tab page", - "settings_pane_header": "New Tab Preferences", - "settings_pane_body2": "Choose what you see on this page.", - "settings_pane_search_header": "Search", - "settings_pane_search_body": "Search the Web from your new tab.", - "settings_pane_topsites_header": "Top Sites", - "settings_pane_topsites_body": "Access the websites you visit most.", - "settings_pane_topsites_options_showmore": "Show two rows", - "settings_pane_bookmarks_header": "Recent Bookmarks", - "settings_pane_bookmarks_body": "Your newly created bookmarks in one handy location.", - "settings_pane_visit_again_header": "Visit Again", + "settings_pane_header": "ចំណង់ចំណូលចិត្ត ផ្ទាំងថ្មី", + "settings_pane_body2": "ជ្រើសរើសអ្វីដែលអ្នកឃើញនៅលើទំព័រនេះ។", + "settings_pane_search_header": "ស្វែងរក", + "settings_pane_search_body": "ស្វែងរកបណ្តាញពីផ្ទាំងថ្មីរបស់អ្នក។", + "settings_pane_topsites_header": "សាយកំពូល", + "settings_pane_topsites_body": "ចូលវេបសាយដែលអ្នកទស្សនាច្រើនបំផុត។", + "settings_pane_topsites_options_showmore": "បង្ហាញជួរដេកពីរ", + "settings_pane_bookmarks_header": "ចំណាំថ្មីៗ", + "settings_pane_bookmarks_body": "ចំណាំថ្មីៗ ដែលបានបង្កើតរបស់អ្នកនៅក្នុងទីតាំងដែលងាយស្រួល។", + "settings_pane_visit_again_header": "ទស្សនាម្ដងទៀត", "settings_pane_visit_again_body": "Firefox will show you parts of your browsing history that you might want to remember or get back to.", - "settings_pane_highlights_header": "Highlights", + "settings_pane_highlights_header": "រឿងសំខាន់ៗ", "settings_pane_highlights_body2": "Find your way back to interesting things you’ve recently visited or bookmarked.", - "settings_pane_highlights_options_bookmarks": "Bookmarks", - "settings_pane_highlights_options_visited": "Visited Sites", - "settings_pane_snippets_header": "Snippets", + "settings_pane_highlights_options_bookmarks": "ចំណាំ", + "settings_pane_highlights_options_visited": "សាយដែលបានទស្សនា", + "settings_pane_snippets_header": "អត្ថបទសង្ខេប", "settings_pane_snippets_body": "Read short and sweet updates from Mozilla about Firefox, internet culture, and the occasional random meme.", - "settings_pane_done_button": "Done", + "settings_pane_done_button": "ធ្វើរួច", "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", - "edit_topsites_button_text": "Edit", + "edit_topsites_button_text": "កែសម្រួល", "edit_topsites_button_label": "Customize your Top Sites section", - "edit_topsites_showmore_button": "Show More", - "edit_topsites_showless_button": "Show Fewer", - "edit_topsites_done_button": "Done", - "edit_topsites_pin_button": "Pin this site", - "edit_topsites_unpin_button": "Unpin this site", - "edit_topsites_edit_button": "Edit this site", - "edit_topsites_dismiss_button": "Dismiss this site", - "edit_topsites_add_button": "Add", - "topsites_form_add_header": "New Top Site", - "topsites_form_edit_header": "Edit Top Site", - "topsites_form_title_placeholder": "Enter a title", - "topsites_form_url_placeholder": "Type or paste a URL", - "topsites_form_add_button": "Add", - "topsites_form_save_button": "Save", - "topsites_form_cancel_button": "Cancel", - "topsites_form_url_validation": "Valid URL required", - "pocket_read_more": "Popular Topics:", - "pocket_read_even_more": "View More Stories", - "pocket_feedback_header": "The best of the web, curated by over 25 million people.", + "edit_topsites_showmore_button": "បង្ហាញច្រើនទៀត", + "edit_topsites_showless_button": "បង្ហាញតិចជាង​នេះ", + "edit_topsites_done_button": "ធ្វើរួច", + "edit_topsites_pin_button": "ខ្ទាស់សាយនេះ", + "edit_topsites_unpin_button": "ដកខ្ទាស់សាយនេះ", + "edit_topsites_edit_button": "កែសម្រួលសាយនេះ", + "edit_topsites_dismiss_button": "ច្រានចោលសាយនេះ", + "edit_topsites_add_button": "បន្ថែម", + "topsites_form_add_header": "សាយកំពូលថ្មី", + "topsites_form_edit_header": "កែសម្រួលសាយកំពូល", + "topsites_form_title_placeholder": "បញ្ចូលចំណងជើង", + "topsites_form_url_placeholder": "វាយបញ្ចូល ឬបិទភ្ជាប់ URL", + "topsites_form_add_button": "បន្ថែម", + "topsites_form_save_button": "រក្សាទុក", + "topsites_form_cancel_button": "បោះបង់", + "topsites_form_url_validation": "ត្រូវការ URL ដែលត្រឹមត្រូវ", + "pocket_read_more": "ប្រធានបទកំពុងពេញនិយម៖", + "pocket_read_even_more": "មើលរឿងរ៉ាវច្រើនទៀត", + "pocket_feedback_header": "បណ្តាញល្អបំផុត ដែលបានវាយតម្លៃដោយមនុស្សជាង 25 លាននាក់។", "pocket_description": "Discover high-quality content you might otherwise miss, with help from Pocket, now part of Mozilla.", "highlights_empty_state": "Start browsing, and we’ll show some of the great articles, videos, and other pages you’ve recently visited or bookmarked here.", "topstories_empty_state": "You’ve caught up. Check back later for more top stories from {provider}. Can’t wait? Select a popular topic to find more great stories from around the web.", - "manual_migration_explanation2": "Try Firefox with the bookmarks, history and passwords from another browser.", - "manual_migration_cancel_button": "No Thanks", - "manual_migration_import_button": "Import Now" + "manual_migration_explanation2": "សាកល្បងប្រើ Firefox ជាមួយចំណាំ ប្រវត្តិ និងពាក្យសម្ងាត់ពីកម្មវិធីរុករកផ្សេងទៀត។", + "manual_migration_cancel_button": "ទេ អរគុណ", + "manual_migration_import_button": "នាំចូលឥឡូវនេះ" }; diff --git a/browser/extensions/activity-stream/prerendered/locales/ko/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/ko/activity-stream-strings.js index d256a3ef86cc..f5c013f5c5d1 100644 --- a/browser/extensions/activity-stream/prerendered/locales/ko/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/ko/activity-stream-strings.js @@ -39,8 +39,8 @@ window.gActivityStreamStrings = { "section_info_send_feedback": "의견 보내기", "section_info_privacy_notice": "개인 정보 보호 정책", "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", - "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories_linktext": "어떻게 작동 하는지 알아봅시다.", + "section_disclaimer_topstories_buttontext": "알겠습니다.", "welcome_title": "새 탭을 소개합니다", "welcome_body": "최근에 방문한 관련있는 즐겨찾기나 글, 동영상, 페이지를 Firefox가 여기에 표시해서 쉽게 다시 찾아볼 수 있게 할 것입니다.", "welcome_label": "하이라이트 확인", @@ -62,12 +62,12 @@ window.gActivityStreamStrings = { "settings_pane_visit_again_body": "Firefox will show you parts of your browsing history that you might want to remember or get back to.", "settings_pane_highlights_header": "하이라이트", "settings_pane_highlights_body2": "Find your way back to interesting things you’ve recently visited or bookmarked.", - "settings_pane_highlights_options_bookmarks": "Bookmarks", - "settings_pane_highlights_options_visited": "Visited Sites", + "settings_pane_highlights_options_bookmarks": "즐겨찾기", + "settings_pane_highlights_options_visited": "방문한 사이트", "settings_pane_snippets_header": "Snippets", "settings_pane_snippets_body": "Read short and sweet updates from Mozilla about Firefox, internet culture, and the occasional random meme.", "settings_pane_done_button": "완료", - "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", + "settings_pane_topstories_options_sponsored": "후원된 스토리", "edit_topsites_button_text": "수정", "edit_topsites_button_label": "상위 사이트 영역 꾸미기", "edit_topsites_showmore_button": "더보기", @@ -90,7 +90,7 @@ window.gActivityStreamStrings = { "pocket_read_even_more": "더 많은 이야기 보기", "pocket_feedback_header": "2천 5백만 명에 의해 추천되는 최고의 웹입니다.", "pocket_description": "Mozilla와 하나가 된 Pocket의 도움으로 놓칠지도 모르는 고품질의 컨텐츠를 접해보세요.", - "highlights_empty_state": "Start browsing, and we’ll show some of the great articles, videos, and other pages you’ve recently visited or bookmarked here.", + "highlights_empty_state": "브라우징을 시작하면 최근 방문하거나 북마크한 좋은 글이나 영상, 페이지를 여기에 보여줍니다.", "topstories_empty_state": "You’ve caught up. Check back later for more top stories from {provider}. Can’t wait? Select a popular topic to find more great stories from around the web.", "manual_migration_explanation2": "다른 브라우저에 있는 북마크, 기록, 비밀번호를 사용해 Firefox를 이용해 보세요.", "manual_migration_cancel_button": "괜찮습니다", diff --git a/browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-prerendered.html index c6d2fc2f597d..f9966385f771 100644 --- a/browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-prerendered.html +++ b/browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-prerendered.html @@ -8,7 +8,7 @@ -

          ເວັບໄຊຕ໌ຍອດນິຍົມ

          Recommended by Pocket

          Popular Topics:

            ຈຸດເດັ່ນ

            +

            ເວັບໄຊຕ໌ຍອດນິຍົມ

            Recommended by Pocket

            ຫົວຂໍ້ຍອດນິຍົມ:

              ລາຍການເດັ່ນ

              diff --git a/browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-strings.js index 41433c13b36a..72425d179554 100644 --- a/browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/lo/activity-stream-strings.js @@ -4,7 +4,7 @@ window.gActivityStreamStrings = { "default_label_loading": "ກຳລັງໂຫລດ…", "header_top_sites": "ເວັບໄຊຕ໌ຍອດນິຍົມ", "header_stories": "Top Stories", - "header_highlights": "ຈຸດເດັ່ນ", + "header_highlights": "ລາຍການເດັ່ນ", "header_visit_again": "Visit Again", "header_bookmarks": "Recent Bookmarks", "header_recommended_by": "Recommended by {provider}", @@ -29,7 +29,7 @@ window.gActivityStreamStrings = { "menu_action_unpin": "Unpin", "confirm_history_delete_p1": "Are you sure you want to delete every instance of this page from your history?", "confirm_history_delete_notice_p2": "This action cannot be undone.", - "menu_action_save_to_pocket": "Save to Pocket", + "menu_action_save_to_pocket": "ບັນທືກໄປທີ່ Pocket", "search_for_something_with": "ຄົ້ນຫາສໍາລັບ {search_term} ດ້ວຍ:", "search_button": "ຊອກ​ຫາ", "search_header": "ຄົ້ນຫາ {search_engine_name}", @@ -64,7 +64,7 @@ window.gActivityStreamStrings = { "settings_pane_highlights_body2": "Find your way back to interesting things you’ve recently visited or bookmarked.", "settings_pane_highlights_options_bookmarks": "Bookmarks", "settings_pane_highlights_options_visited": "Visited Sites", - "settings_pane_snippets_header": "Snippets", + "settings_pane_snippets_header": "ສ່ວນຍ່ອຍ", "settings_pane_snippets_body": "Read short and sweet updates from Mozilla about Firefox, internet culture, and the occasional random meme.", "settings_pane_done_button": "ສຳເລັດ", "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", @@ -80,13 +80,13 @@ window.gActivityStreamStrings = { "edit_topsites_add_button": "Add", "topsites_form_add_header": "New Top Site", "topsites_form_edit_header": "Edit Top Site", - "topsites_form_title_placeholder": "Enter a title", + "topsites_form_title_placeholder": "ປ້ອນຊື່ເລື່ອງ", "topsites_form_url_placeholder": "Type or paste a URL", "topsites_form_add_button": "Add", "topsites_form_save_button": "Save", "topsites_form_cancel_button": "Cancel", "topsites_form_url_validation": "Valid URL required", - "pocket_read_more": "Popular Topics:", + "pocket_read_more": "ຫົວຂໍ້ຍອດນິຍົມ:", "pocket_read_even_more": "View More Stories", "pocket_feedback_header": "The best of the web, curated by over 25 million people.", "pocket_description": "Discover high-quality content you might otherwise miss, with help from Pocket, now part of Mozilla.", @@ -94,7 +94,5 @@ window.gActivityStreamStrings = { "topstories_empty_state": "You’ve caught up. Check back later for more top stories from {provider}. Can’t wait? Select a popular topic to find more great stories from around the web.", "manual_migration_explanation2": "Try Firefox with the bookmarks, history and passwords from another browser.", "manual_migration_cancel_button": "No Thanks", - "manual_migration_import_button": "Import Now", - "settings_pane_body": "ເລືອກສິ່ງທີ່ທ່ານເຫັນເມື່ອທ່ານເປີດແທັບໃຫມ່.", - "settings_pane_highlights_body": "ຍ້ອນຄືນກັບໄປເບິງປະຫວັດການທ່ອງເວັບທີ່ຫາກາເຂົ້າໄປ ແລະ ບຸກມາກທີ່ໄດ້ຮັບການສ້າງຂື້ນມາໃຫມ່ຂອງທ່ານ." + "manual_migration_import_button": "ນຳເຂົ້າຕອນນີ້" }; diff --git a/browser/extensions/activity-stream/prerendered/locales/lt/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/lt/activity-stream-strings.js index 4203f29f50a5..bc93a72d6a28 100644 --- a/browser/extensions/activity-stream/prerendered/locales/lt/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/lt/activity-stream-strings.js @@ -38,9 +38,9 @@ window.gActivityStreamStrings = { "section_info_option": "Informacija", "section_info_send_feedback": "Siųsti atsiliepimą", "section_info_privacy_notice": "Privatumo nuostatai", - "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", - "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories": "Įdomiausi saityno straipsniai, parinkti pagal jūsų skaitymo įpročius. Iš „Pocket“, kuri dabar priklauso „Mozillai“.", + "section_disclaimer_topstories_linktext": "Sužinokite, kaip tai veikia.", + "section_disclaimer_topstories_buttontext": "Gerai, supratau", "welcome_title": "Sveiki, čia nauja kortelė", "welcome_body": "„Firefox“ naudos šią vietą jums aktualiausių adresyno įrašų, straipsnių, vaizdo įrašų bei neseniai lankytų tinklalapių rodymui, kad galėtumėte lengvai į juos sugrįžti.", "welcome_label": "Nustatomi jūsų akcentai", @@ -67,7 +67,7 @@ window.gActivityStreamStrings = { "settings_pane_snippets_header": "Iškarpos", "settings_pane_snippets_body": "Skaitykite trumpas ir mielas naujienas iš „Mozillos“ apie „Firefox“, interneto kultūrą bei atsitiktinį memą.", "settings_pane_done_button": "Atlikta", - "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", + "settings_pane_topstories_options_sponsored": "Rodyti rėmėjų straipsnius", "edit_topsites_button_text": "Keisti", "edit_topsites_button_label": "Tinkinkite savo lankomiausių svetainių skiltį", "edit_topsites_showmore_button": "Rodyti daugiau", diff --git a/browser/extensions/activity-stream/prerendered/locales/ml/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/ml/activity-stream-strings.js index 53e2dc99f0d1..56634ad7f10a 100644 --- a/browser/extensions/activity-stream/prerendered/locales/ml/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/ml/activity-stream-strings.js @@ -38,9 +38,9 @@ window.gActivityStreamStrings = { "section_info_option": "വിവരം", "section_info_send_feedback": "ഫീഡ്ബാക്ക് അയയ്ക്കുക", "section_info_privacy_notice": "സ്വകാര്യതാ അറിയിപ്പ്", - "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", - "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories": "വെബിലെ ഏറ്റവും രസകരമായ അറിവുകൾ, നിങ്ങൾ വായിച്ചവ അടിസ്ഥാനമാക്കി തിരഞ്ഞെടുത്തത്. ഇപ്പോൾ മോസില്ലയുടെ ഭാഗമായ പോക്കറ്റിൽ നിന്നും.", + "section_disclaimer_topstories_linktext": "എങ്ങനെ പ്രവര്‍ത്തിക്കുന്നു എന്ന് കാണുക.", + "section_disclaimer_topstories_buttontext": "ശരി, മനസ്സിലായി", "welcome_title": "പുതിയ ജാലകത്തിലേക്കു സ്വാഗതം", "welcome_body": "നിങ്ങളുടെ ഏറ്റവും ശ്രദ്ധേയമായ അടയാളങ്ങൾ, ലേഖനങ്ങൾ, വീഡിയോകൾ, കൂടാതെ നിങ്ങൾ സമീപകാലത്ത് സന്ദർശിച്ച താളുകൾ എന്നിവ കാണിക്കുന്നതിനായി ഫയർഫോക്സ് ഈ ഇടം ഉപയോഗിക്കും, അതിനാൽ നിങ്ങൾക്ക് എളുപ്പത്തിൽ അവയിലേക്ക് തിരിച്ചു പോകാം.", "welcome_label": "താങ്കളുടെ ഹൈലൈറ്റ്സ് തിരിച്ചറിയുന്നു", @@ -67,7 +67,7 @@ window.gActivityStreamStrings = { "settings_pane_snippets_header": "ലഘു കുറിപ്പുകൾ", "settings_pane_snippets_body": "മോസില്ലയിൽ നിന്നും ഫയർഫോക്സ്, ഇന്റർനെറ്റ് സംസ്കാരം, വല്ലപ്പോഴുമുള്ള ക്രമമില്ലാത്ത മെമെ, എന്നിവയിൽ ചെറുതും മധുരവുമായ പരിഷ്കരണങ്ങൾ വായിക്കുക.", "settings_pane_done_button": "തീർന്നു", - "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", + "settings_pane_topstories_options_sponsored": "സ്പോൺസർ ചെയ്തവ കാണിക്കുക", "edit_topsites_button_text": "തിരുത്തുക", "edit_topsites_button_label": "നിങ്ങളുടെ മുന്നേറിയ സൈറ്റുകളുടെ വിഭാഗം ഇഷ്ടാനുസൃതമാക്കുക", "edit_topsites_showmore_button": "കൂടുതൽ കാണിക്കുക", diff --git a/browser/extensions/activity-stream/prerendered/locales/pt-BR/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/pt-BR/activity-stream-strings.js index 7aa50614e4d7..d748fa38d0df 100644 --- a/browser/extensions/activity-stream/prerendered/locales/pt-BR/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/pt-BR/activity-stream-strings.js @@ -27,7 +27,7 @@ window.gActivityStreamStrings = { "menu_action_delete": "Excluir do histórico", "menu_action_pin": "Fixar", "menu_action_unpin": "Desafixar", - "confirm_history_delete_p1": "Você tem certeza que deseja deletar todas as ocorrências dessa página do seu histórico?", + "confirm_history_delete_p1": "Você tem certeza que deseja excluir todas as instâncias desta página do seu histórico?", "confirm_history_delete_notice_p2": "Essa ação não pode ser desfeita.", "menu_action_save_to_pocket": "Salvar no Pocket", "search_for_something_with": "Pesquisar por {search_term} com:", @@ -36,7 +36,7 @@ window.gActivityStreamStrings = { "search_web_placeholder": "Pesquisar na Web", "search_settings": "Alterar configurações de pesquisa", "section_info_option": "Info", - "section_info_send_feedback": "Enviar opinião", + "section_info_send_feedback": "Enviar feedback", "section_info_privacy_notice": "Política de Privacidade", "section_disclaimer_topstories": "As histórias mais interessantes na web, selecionadas baseadas no que você lê. Do Pocket, agora parte da Mozilla.", "section_disclaimer_topstories_linktext": "Saiba como funciona.", diff --git a/browser/extensions/activity-stream/prerendered/locales/pt-PT/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/pt-PT/activity-stream-strings.js index 77d612902447..230440c275a5 100644 --- a/browser/extensions/activity-stream/prerendered/locales/pt-PT/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/pt-PT/activity-stream-strings.js @@ -41,7 +41,7 @@ window.gActivityStreamStrings = { "section_disclaimer_topstories": "As histórias mais interessantes na web, selecionadas baseadas no que você lê. Do Pocket, agora parte da Mozilla.", "section_disclaimer_topstories_linktext": "Saiba como funciona.", "section_disclaimer_topstories_buttontext": "Ok, entendi", - "welcome_title": "Bem-vindo ao novo separador", + "welcome_title": "Bem-vindo(a) ao novo separador", "welcome_body": "O Firefox irá utilizar este espaço para lhe mostrar os seus marcadores, artigos, vídeos, e páginas mais relevantes que visitou recentemente, para que possa regressar a estes mais facilmente.", "welcome_label": "A identificar os seus destaques", "time_label_less_than_minute": "<1m", @@ -52,7 +52,7 @@ window.gActivityStreamStrings = { "settings_pane_header": "Preferências de novo separador", "settings_pane_body2": "Escolha o que vê nesta página.", "settings_pane_search_header": "Pesquisa", - "settings_pane_search_body": "Pesquise na Web a partir do seu novo separador.", + "settings_pane_search_body": "Pesquise na Web a partir do seu 'Novo separador'.", "settings_pane_topsites_header": "Sites mais visitados", "settings_pane_topsites_body": "Aceda aos websites que mais visita.", "settings_pane_topsites_options_showmore": "Mostrar duas linhas", @@ -93,6 +93,6 @@ window.gActivityStreamStrings = { "highlights_empty_state": "Comece a navegar, e iremos mostrar-lhe alguns dos ótimos artigos, vídeos, e outras páginas que visitou recentemente ou adicionou aos marcadores aqui.", "topstories_empty_state": "Já apanhou tudo. Verifique mais tarde para mais histórias principais de {provider}. Não pode esperar? Selecione um tópico popular para encontrar mais boas histórias de toda a web.", "manual_migration_explanation2": "Experimente o Firefox com marcadores, histórico e palavras-passe de outro navegador.", - "manual_migration_cancel_button": "Não obrigado", + "manual_migration_cancel_button": "Não, obrigado", "manual_migration_import_button": "Importar agora" }; diff --git a/browser/extensions/activity-stream/prerendered/locales/ro/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/ro/activity-stream-strings.js index 23b46a180adb..ce6f6970191f 100644 --- a/browser/extensions/activity-stream/prerendered/locales/ro/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/ro/activity-stream-strings.js @@ -38,9 +38,9 @@ window.gActivityStreamStrings = { "section_info_option": "Informații", "section_info_send_feedback": "Trimite feedback", "section_info_privacy_notice": "Politica de confidențialitate", - "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", - "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories": "Cele mai interesante articole de pe web, alese pe baza lucrurilor pe care le citești. De la Pocket, acum parte din Mozilla.", + "section_disclaimer_topstories_linktext": "Află cum funcționează.", + "section_disclaimer_topstories_buttontext": "Ok, am înțeles", "welcome_title": "Bun venit în noua filă", "welcome_body": "Firefox va folosi acest spațiu pentru a arăta cele mai relevante semne de carte, articole, videouri și pagini vizitate recent pentru a reveni la acestea ușor.", "welcome_label": "Se identifică evidențierile tale", @@ -61,13 +61,13 @@ window.gActivityStreamStrings = { "settings_pane_visit_again_header": "Vizitează din nou", "settings_pane_visit_again_body": "Firefox îți va arăta părți din istoricul navigării tale la care ai vrea să revii mai târziu.", "settings_pane_highlights_header": "Evidențieri", - "settings_pane_highlights_body2": "Find your way back to interesting things you’ve recently visited or bookmarked.", + "settings_pane_highlights_body2": "Regăsește lucrurile interesante pe care le-ai vizitat sau marcat recent.", "settings_pane_highlights_options_bookmarks": "Marcaje", "settings_pane_highlights_options_visited": "Site-uri vizitate", - "settings_pane_snippets_header": "Snippets", - "settings_pane_snippets_body": "Read short and sweet updates from Mozilla about Firefox, internet culture, and the occasional random meme.", + "settings_pane_snippets_header": "Fragmente", + "settings_pane_snippets_body": "Citește actualizări scurte de la Mozilla despre Firefox, cultura internetului și meme-ul ocazional.", "settings_pane_done_button": "Gata", - "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", + "settings_pane_topstories_options_sponsored": "Arată articolele sponsorizate", "edit_topsites_button_text": "Editează", "edit_topsites_button_label": "Particularizează secțiunea site-urilor de top", "edit_topsites_showmore_button": "Arată mai mult", @@ -89,9 +89,9 @@ window.gActivityStreamStrings = { "pocket_read_more": "Subiecte populare:", "pocket_read_even_more": "Vezi mai multe articole", "pocket_feedback_header": "Cel mai bun de pe web, întreţinut de peste 25 de milioane de oameni.", - "pocket_description": "Discover high-quality content you might otherwise miss, with help from Pocket, now part of Mozilla.", - "highlights_empty_state": "Start browsing, and we’ll show some of the great articles, videos, and other pages you’ve recently visited or bookmarked here.", - "topstories_empty_state": "You’ve caught up. Check back later for more top stories from {provider}. Can’t wait? Select a popular topic to find more great stories from around the web.", + "pocket_description": "Descoperă conținut de calitate pe care l-ai putea rata, cu ajutorul Pocket, acum parte din Mozilla.", + "highlights_empty_state": "Începe să navighezi și noi îți vom arăta articole interesante, videouri sau alte pagini pe care le-ai vizitat sau marcat recent.", + "topstories_empty_state": "Ai ajuns la capăt. Revino mai târziu pentru alte articole de la {provider}. Nu mai vrei să aștepți? Alege un subiect popular și găsește alte articole interesante de pe web.", "manual_migration_explanation2": "Încearcă Firefox cu marcajele, istoricul și parolele din alt navigator.", "manual_migration_cancel_button": "Nu, mulțumesc", "manual_migration_import_button": "Importă acum" diff --git a/browser/extensions/activity-stream/prerendered/locales/si/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/si/activity-stream-prerendered.html new file mode 100644 index 000000000000..35e7ec576138 --- /dev/null +++ b/browser/extensions/activity-stream/prerendered/locales/si/activity-stream-prerendered.html @@ -0,0 +1,37 @@ + + + + + + + + + + +

              ප්‍රමුඛ අඩවි

              Pocket විසින් නිර්දේශිතයි

              ජනප්‍රිය මාතෘකා:

                ඉස්මතු කිරීම්

                +
                +
                +
                + + + diff --git a/browser/extensions/activity-stream/prerendered/locales/si/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/si/activity-stream-strings.js new file mode 100644 index 000000000000..58fe3b49cc28 --- /dev/null +++ b/browser/extensions/activity-stream/prerendered/locales/si/activity-stream-strings.js @@ -0,0 +1,98 @@ +// Note - this is a generated file. +window.gActivityStreamStrings = { + "newtab_page_title": "නව ටැබය", + "default_label_loading": "පූරණය වෙමින්…", + "header_top_sites": "ප්‍රමුඛ අඩවි", + "header_stories": "ප්‍රමුඛ පුවත්", + "header_highlights": "ඉස්මතු කිරීම්", + "header_visit_again": "යළි පිවිසෙන්න", + "header_bookmarks": "නැවුම් පිටු සලකුණු", + "header_recommended_by": "{provider} විසින් නිර්දේශිතයි", + "header_bookmarks_placeholder": "ඔබ සතුව තවම පිටුසලකුණු නැත.", + "header_stories_from": "සිට​", + "type_label_visited": "ප්‍රවේශිත", + "type_label_bookmarked": "පිටු සලකුණු තැබූ", + "type_label_synced": "වෙනත් උපාංගයක් වෙතින් සමකාලීන​ කර ඇත​", + "type_label_recommended": "Trending", + "type_label_open": "විවෘත", + "type_label_topic": "මාතෘකාව", + "type_label_now": "දැන්", + "menu_action_bookmark": "පිටු සලකුණ", + "menu_action_remove_bookmark": "පිටු සලකුණ ඉවත් කරන්න", + "menu_action_copy_address": "ලිපිනය පිටපත් කරන්න", + "menu_action_email_link": "විද්‍යුත් තැපැල් සබැඳි…", + "menu_action_open_new_window": "නව කවුළුවක විවෘත කරන්න", + "menu_action_open_private_window": "නව පුද්ගලික කවුළුවක විවෘත කරන්න", + "menu_action_dismiss": "ඉවත් කරන්න", + "menu_action_delete": "අතිතයෙන් මකන්න කරන්න", + "menu_action_pin": "ඇමිණීම", + "menu_action_unpin": "ඇමුණුම ඉවත් කරන්න", + "confirm_history_delete_p1": "ඔබට මෙම පිටුවට අදාල සියලුම සිදුවීම් ඔබේ අතීතයන් මැකීමට අවශ්‍ය ද?", + "confirm_history_delete_notice_p2": "මෙම ක්‍රියාව අහෝසි කළ නොහැක.", + "menu_action_save_to_pocket": "Save to Pocket", + "search_for_something_with": "{search_term} සදහා සෙවීමට භාවිත කළ යුත්තේ:", + "search_button": "සොයන්න", + "search_header": "{search_engine_name} ෙසවුම", + "search_web_placeholder": "ජාලය තුළ සොයන්න", + "search_settings": "සෙවුම් සැකසුම් වෙනස් කරන්න", + "section_info_option": "තොරතුරු", + "section_info_send_feedback": "ප්‍රතිචාරය යවන්න", + "section_info_privacy_notice": "පෞද්ගලිකත්ව දැනුම්දීම්", + "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", + "section_disclaimer_topstories_linktext": "එය ක්‍රියාකරන්නේ කෙසේදැයි අධ්‍යපනය කරන්න.", + "section_disclaimer_topstories_buttontext": "හරි, තේරුණා", + "welcome_title": "නව ටැබයට සාදරයෙන් පිළිගනිමු", + "welcome_body": "ඔබට පහසුවෙන් යළි භාවිතයට පහසු කරවීමට, Firefox මෙම ඉඩ ඔබට වඩාත් අදාල පිටු සළකුණු, ලිපි, විඩියෝ සහ ඔබ මෑතකදී පිවිසි පිටු පෙන්වීම සදහා භාවිත කරයි.", + "welcome_label": "ඔබේ ඉස්මතු කිරීම් හදුනාගනිමින්", + "time_label_less_than_minute": "<1m", + "time_label_minute": "{number} මිනිත්තු", + "time_label_hour": "{number}පැය", + "time_label_day": "{number}දින", + "settings_pane_button_label": "ඔබේ නව ටැබ පිටුව රුචිකරණය කරන්න", + "settings_pane_header": "නව ටැබ අභිප්‍රේත", + "settings_pane_body2": "මෙම පිටුවේ ඔබ දැකිය යුතු දේ තෝරන්න.", + "settings_pane_search_header": "සොයන්න", + "settings_pane_search_body": "ඔබේ නව ටැබයෙන් වෙබ් සෙවීම.", + "settings_pane_topsites_header": "ප්‍රමුඛ අඩවි", + "settings_pane_topsites_body": "ඔබ නිරතුරුව පිවිසෙන වෙබ් අඩවි වෙත ප්‍රවේශය.", + "settings_pane_topsites_options_showmore": "පේළි දෙකක් පෙන්වන්න", + "settings_pane_bookmarks_header": "නැවුම් පිටු සලකුණු", + "settings_pane_bookmarks_body": "ඔබේ නැවුම් පිටු සලකුණු එක් ස්ථානයක.", + "settings_pane_visit_again_header": "යළි පිවිසෙන්න", + "settings_pane_visit_again_body": "Firefox will show you parts of your browsing history that you might want to remember or get back to.", + "settings_pane_highlights_header": "ඉස්මතු කිරීම්", + "settings_pane_highlights_body2": "Find your way back to interesting things you’ve recently visited or bookmarked.", + "settings_pane_highlights_options_bookmarks": "පිටු සලකුණු", + "settings_pane_highlights_options_visited": "පිවිසුණු අඩවි", + "settings_pane_snippets_header": "Snippets", + "settings_pane_snippets_body": "Read short and sweet updates from Mozilla about Firefox, internet culture, and the occasional random meme.", + "settings_pane_done_button": "", + "settings_pane_topstories_options_sponsored": "අනුග්‍රහක පුවත් පෙන්වන්න", + "edit_topsites_button_text": "සැකසුම්", + "edit_topsites_button_label": "Customize your Top Sites section", + "edit_topsites_showmore_button": "තවත් පෙන්වන්න", + "edit_topsites_showless_button": "අඩුවෙන් පෙන්වන්න", + "edit_topsites_done_button": "කළා", + "edit_topsites_pin_button": "Pin this site", + "edit_topsites_unpin_button": "Unpin this site", + "edit_topsites_edit_button": "මෙම අඩවිය සකසන්න", + "edit_topsites_dismiss_button": "මෙම අඩවිය ඉවත ලන්න", + "edit_topsites_add_button": "එක් කරන්න", + "topsites_form_add_header": "නව ප්‍රමුඛ අඩවියක්", + "topsites_form_edit_header": "ප්‍රමුඛ අඩවිය සකසන්න", + "topsites_form_title_placeholder": "සිරස්තල එක් කරන්න", + "topsites_form_url_placeholder": "URL එකක් ඇතුළත් කරන්න", + "topsites_form_add_button": "එක් කරන්න", + "topsites_form_save_button": "සුරකින්න", + "topsites_form_cancel_button": "අවලංගු කරන්න", + "topsites_form_url_validation": "වලංගු URL එකක් අවශ්‍ය වේ", + "pocket_read_more": "ජනප්‍රිය මාතෘකා:", + "pocket_read_even_more": "තවත් බොහෝ දැ", + "pocket_feedback_header": "The best of the web, curated by over 25 million people.", + "pocket_description": "Discover high-quality content you might otherwise miss, with help from Pocket, now part of Mozilla.", + "highlights_empty_state": "Start browsing, and we’ll show some of the great articles, videos, and other pages you’ve recently visited or bookmarked here.", + "topstories_empty_state": "You’ve caught up. Check back later for more top stories from {provider}. Can’t wait? Select a popular topic to find more great stories from around the web.", + "manual_migration_explanation2": "Firefox වෙනත් ගවේශයකය පිටය සලකුණු, අතීතය සහ මුරපද සමග උත්සාහ කර බලන්න.", + "manual_migration_cancel_button": "එපා, ස්තුතියි", + "manual_migration_import_button": "දැන් ආයාත කරන්න" +}; diff --git a/browser/extensions/activity-stream/prerendered/locales/si/activity-stream.html b/browser/extensions/activity-stream/prerendered/locales/si/activity-stream.html new file mode 100644 index 000000000000..1ff15a123ccf --- /dev/null +++ b/browser/extensions/activity-stream/prerendered/locales/si/activity-stream.html @@ -0,0 +1,36 @@ + + + + + + + + + + +
                +
                +
                +
                + + + diff --git a/browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-prerendered.html b/browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-prerendered.html index 8da0124eb13a..08de0ad74d93 100644 --- a/browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-prerendered.html +++ b/browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-prerendered.html @@ -8,7 +8,7 @@ -

                சிறந்த தளங்கள்

                Pocket என்பவரால் பரிந்துரைக்கப்பட்டது

                பிரபலமான தலைப்புகள்:

                  Highlights

                  +

                  சிறந்த தளங்கள்

                  Pocket என்பவரால் பரிந்துரைக்கப்பட்டது

                  பிரபலமான தலைப்புகள்:

                    மிளிர்ப்புகள்

                    diff --git a/browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-strings.js index d57ded84be36..1f1585656e6b 100644 --- a/browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/ta/activity-stream-strings.js @@ -4,7 +4,7 @@ window.gActivityStreamStrings = { "default_label_loading": "ஏற்றுகிறது…", "header_top_sites": "சிறந்த தளங்கள்", "header_stories": "முக்கிய கதைகள்", - "header_highlights": "Highlights", + "header_highlights": "மிளிர்ப்புகள்", "header_visit_again": "மீண்டும் வருக", "header_bookmarks": "சமீபத்திய புத்தகக்குறிகள்", "header_recommended_by": "{provider} என்பவரால் பரிந்துரைக்கப்பட்டது", @@ -36,8 +36,8 @@ window.gActivityStreamStrings = { "search_web_placeholder": "இணையத்தில் தேடு", "search_settings": "தேடல் அமைவுகளை மாற்று", "section_info_option": "தகவல்", - "section_info_send_feedback": "Send Feedback", - "section_info_privacy_notice": "Privacy Notice", + "section_info_send_feedback": "பின்னூட்டம் அனுப்பு", + "section_info_privacy_notice": "தனியுரிம கொள்கை", "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", "section_disclaimer_topstories_linktext": "Learn how it works.", "section_disclaimer_topstories_buttontext": "Okay, got it", @@ -60,10 +60,10 @@ window.gActivityStreamStrings = { "settings_pane_bookmarks_body": "ஒரு வசதியான இடத்தில் உங்கள் புதிதாக உருவாக்கப்பட்ட புத்தகக்குறிகள்.", "settings_pane_visit_again_header": "மீண்டும் வருக", "settings_pane_visit_again_body": "பயர்பாக்ஸ் நீங்கள் நினைவுப்படுத்த (அ) திரும்பப் பெற விரும்பும் உங்கள் உலாவல் வரலாற்றின் சில பகுதிகளைக் காட்டும்.", - "settings_pane_highlights_header": "Highlights", + "settings_pane_highlights_header": "மிளிர்ப்புகள்", "settings_pane_highlights_body2": "Find your way back to interesting things you’ve recently visited or bookmarked.", - "settings_pane_highlights_options_bookmarks": "Bookmarks", - "settings_pane_highlights_options_visited": "Visited Sites", + "settings_pane_highlights_options_bookmarks": "புத்தகக்குறிகள்", + "settings_pane_highlights_options_visited": "பார்வையிடப்பட்ட தளம்", "settings_pane_snippets_header": "Snippets", "settings_pane_snippets_body": "Read short and sweet updates from Mozilla about Firefox, internet culture, and the occasional random meme.", "settings_pane_done_button": "முடிந்தது", @@ -94,10 +94,5 @@ window.gActivityStreamStrings = { "topstories_empty_state": "You’ve caught up. Check back later for more top stories from {provider}. Can’t wait? Select a popular topic to find more great stories from around the web.", "manual_migration_explanation2": "Try Firefox with the bookmarks, history and passwords from another browser.", "manual_migration_cancel_button": "பரவாயில்லை", - "manual_migration_import_button": "இப்போது இறக்கு", - "settings_pane_body": "ஒரு புதிய கீற்றைத் திறக்கும்போது நீங்கள் பார்ப்பதை தேர்க.", - "settings_pane_pocketstories_header": "முக்கிய கதைகள்", - "settings_pane_pocketstories_body": "Pocket, ஒரு மொசில்லா குடும்ப உறுப்பினராக, உயர்தர உள்ளடக்கங்களுடன் இணைய உதவுகிறது, இது இல்லையேல் அது சாத்தியமாகது.", - "pocket_feedback_body": "Pocket, ஒரு மொசில்லா குடும்ப உறுப்பினராக, உயர்தர உள்ளடக்கங்களுடன் இணைய உதவுகிறது, இது இல்லையேல் அது சாத்தியமாகது.", - "pocket_send_feedback": "கருத்துகளைத் தெறிவிக்கவும்" + "manual_migration_import_button": "இப்போது இறக்கு" }; diff --git a/browser/extensions/activity-stream/prerendered/locales/th/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/th/activity-stream-strings.js index cbc4cc05d739..c30b6f721d34 100644 --- a/browser/extensions/activity-stream/prerendered/locales/th/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/th/activity-stream-strings.js @@ -67,7 +67,7 @@ window.gActivityStreamStrings = { "settings_pane_snippets_header": "ส่วนย่อย", "settings_pane_snippets_body": "อ่านข้อมูลอัปเดตที่สั้นและไพเราะจาก Mozilla เกี่ยวกับ Firefox, วัฒนธรรมอินเทอร์เน็ต และมีมแบบสุ่มเป็นครั้งคราว", "settings_pane_done_button": "เสร็จสิ้น", - "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", + "settings_pane_topstories_options_sponsored": "แสดงเรื่องราวที่ได้รับการสนับสนุน", "edit_topsites_button_text": "แก้ไข", "edit_topsites_button_label": "ปรับแต่งส่วนไซต์เด่นของคุณ", "edit_topsites_showmore_button": "แสดงเพิ่มเติม", diff --git a/browser/extensions/activity-stream/prerendered/locales/tl/activity-stream-strings.js b/browser/extensions/activity-stream/prerendered/locales/tl/activity-stream-strings.js index 3755eccc73f2..b65ea4741d29 100644 --- a/browser/extensions/activity-stream/prerendered/locales/tl/activity-stream-strings.js +++ b/browser/extensions/activity-stream/prerendered/locales/tl/activity-stream-strings.js @@ -38,9 +38,9 @@ window.gActivityStreamStrings = { "section_info_option": "Impormasyon", "section_info_send_feedback": "Magbigay ng Feedback", "section_info_privacy_notice": "Abiso sa Privacy", - "section_disclaimer_topstories": "The most interesting stories on the web, selected based on what you read. From Pocket, now part of Mozilla.", - "section_disclaimer_topstories_linktext": "Learn how it works.", - "section_disclaimer_topstories_buttontext": "Okay, got it", + "section_disclaimer_topstories": "Ang pinaka-kagiliw-giliw na mga kwento sa web, pinili batay sa kung ano ang iyong nabasa. Mula sa Pocket, bahagi na ngayon ng Mozilla.", + "section_disclaimer_topstories_linktext": "Alamin kung paano ito gumagana.", + "section_disclaimer_topstories_buttontext": "Sige, nakuha ko", "welcome_title": "Maligayang pagdating sa bagong tab", "welcome_body": "Firefox ay gagamit ng puwang upang ipakita ang iyong mga pinaka-kaugnay na bookmark, artikulo, video, at mga pahina ng kamakailan na iyong binisita, kaya maaari kang bumalik sa mga ito ng madali.", "welcome_label": "Ang pagkilala sa iyong Highlights", @@ -67,7 +67,7 @@ window.gActivityStreamStrings = { "settings_pane_snippets_header": "Mga snippet", "settings_pane_snippets_body": "Magbasa ng maikli at matamis na mga update mula sa Mozilla tungkol sa Firefox, kultura sa internet, at paminsan-minsang random na meme.", "settings_pane_done_button": "Tapos", - "settings_pane_topstories_options_sponsored": "Show Sponsored Stories", + "settings_pane_topstories_options_sponsored": "Ipakita ang Mga Na-sponsor na Kuwento", "edit_topsites_button_text": "I-edit", "edit_topsites_button_label": "I-customize ang iyong Tuktok na mga seksyon ng Sites", "edit_topsites_showmore_button": "Magpakita ng higit pa", diff --git a/browser/extensions/activity-stream/test/unit/lib/NewTabInit.test.js b/browser/extensions/activity-stream/test/unit/lib/NewTabInit.test.js index 51ba91a891fd..8a17b8d87940 100644 --- a/browser/extensions/activity-stream/test/unit/lib/NewTabInit.test.js +++ b/browser/extensions/activity-stream/test/unit/lib/NewTabInit.test.js @@ -19,50 +19,6 @@ describe("NewTabInit", () => { const resp = ac.SendToContent({type: at.NEW_TAB_INITIAL_STATE, data: STATE}, 123); assert.calledWith(store.dispatch, resp); }); - describe("about:home search auto focus", () => { - let action; - beforeEach(() => { - STATE.Prefs = { - values: { - "aboutHome.autoFocus": true, - "showSearch": true - } - }; - action = { - type: at.NEW_TAB_INIT, - data: { - url: "about:home", - browser: {focus: sinon.spy()} - } - }; - }); - it("should focus the content browser when NEW_TAB_INIT", () => { - instance.onAction(action); - - assert.calledOnce(action.data.browser.focus); - }); - it("should NOT focus the content browser when NEW_TAB_INIT for about:newtab", () => { - action.data.url = "about:newtab"; - - instance.onAction(action); - - assert.notCalled(action.data.browser.focus); - }); - it("should NOT focus the content browser when NEW_TAB_INIT when autoFocus pref is off", () => { - STATE.Prefs.values["aboutHome.autoFocus"] = false; - - instance.onAction(action); - - assert.notCalled(action.data.browser.focus); - }); - it("should NOT focus the content browser when NEW_TAB_INIT when there's no search", () => { - STATE.Prefs.values.showSearch = false; - - instance.onAction(action); - - assert.notCalled(action.data.browser.focus); - }); - }); describe("early / simulated new tabs", () => { const simulateTabInit = portID => instance.onAction({ type: at.NEW_TAB_INIT, diff --git a/browser/extensions/activity-stream/test/unit/lib/SnippetsFeed.test.js b/browser/extensions/activity-stream/test/unit/lib/SnippetsFeed.test.js index c892ea4d9262..4a3632359916 100644 --- a/browser/extensions/activity-stream/test/unit/lib/SnippetsFeed.test.js +++ b/browser/extensions/activity-stream/test/unit/lib/SnippetsFeed.test.js @@ -88,6 +88,15 @@ describe("SnippetsFeed", () => { assert.calledWith(feed.store.dispatch, ac.BroadcastToContent({type: at.SNIPPETS_RESET})); }); + it("should broadcast a SNIPPET_BLOCKED when a SNIPPETS_BLOCKLIST_UPDATED is received", () => { + const feed = new SnippetsFeed(); + feed.store = {dispatch: sandbox.stub()}; + const blockList = ["foo", "bar", "baz"]; + + feed.onAction({type: at.SNIPPETS_BLOCKLIST_UPDATED, data: blockList}); + + assert.calledWith(feed.store.dispatch, ac.BroadcastToContent({type: at.SNIPPET_BLOCKED, data: blockList})); + }); it("should dispatch an update event when the Search observer is called", async () => { const feed = new SnippetsFeed(); feed.store = {dispatch: sandbox.stub()}; diff --git a/browser/extensions/activity-stream/test/unit/lib/TelemetryFeed.test.js b/browser/extensions/activity-stream/test/unit/lib/TelemetryFeed.test.js index 156925062104..493bbd3afcfa 100644 --- a/browser/extensions/activity-stream/test/unit/lib/TelemetryFeed.test.js +++ b/browser/extensions/activity-stream/test/unit/lib/TelemetryFeed.test.js @@ -132,7 +132,7 @@ describe("TelemetryFeed", () => { const session2 = instance.addSession("foo", "about:home"); - assert.propertyNotVal(session2.perf, "load_trigger_type", + assert.notPropertyVal(session2.perf, "load_trigger_type", "first_window_opened"); }); it("should set load_trigger_ts to the value of perfService.timeOrigin", () => { diff --git a/browser/extensions/formautofill/FormAutofillHandler.jsm b/browser/extensions/formautofill/FormAutofillHandler.jsm index 9787c693475f..2b1490952645 100644 --- a/browser/extensions/formautofill/FormAutofillHandler.jsm +++ b/browser/extensions/formautofill/FormAutofillHandler.jsm @@ -411,7 +411,7 @@ class FormAutofillSection { this.changeFieldState(fieldDetail, FIELD_STATES.AUTO_FILLED); } if (fieldDetail.state == FIELD_STATES.AUTO_FILLED) { - element.addEventListener("input", this); + element.addEventListener("input", this, {mozSystemGroup: true}); } } } @@ -513,8 +513,8 @@ class FormAutofillSection { element instanceof Ci.nsIDOMHTMLInputElement) { element.setUserInput(""); } - this.changeFieldState(fieldDetail, FIELD_STATES.NORMAL); } + this.resetFieldStates(); } /** @@ -557,7 +557,7 @@ class FormAutofillSection { resetFieldStates() { for (let fieldDetail of this._validDetails) { const element = fieldDetail.elementWeakRef.get(); - element.removeEventListener("input", this); + element.removeEventListener("input", this, {mozSystemGroup: true}); this.changeFieldState(fieldDetail, FIELD_STATES.NORMAL); } this.address.filledRecordGUID = null; @@ -750,7 +750,7 @@ class FormAutofillSection { if (!targetSet.fieldDetails.some(detail => detail.state == FIELD_STATES.AUTO_FILLED)) { targetSet.filledRecordGUID = null; } - target.removeEventListener("input", this); + target.removeEventListener("input", this, {mozSystemGroup: true}); break; } } @@ -876,7 +876,7 @@ class FormAutofillHandler { if (!input) { continue; } - input.addEventListener("input", this); + input.addEventListener("input", this, {mozSystemGroup: true}); } this.fieldDetails = allValidDetails; @@ -954,16 +954,16 @@ class FormAutofillHandler { } // Unregister listeners once no field is in AUTO_FILLED state. if (!this.hasFilledSection()) { - this.form.rootElement.removeEventListener("input", onChangeHandler); - this.form.rootElement.removeEventListener("reset", onChangeHandler); + this.form.rootElement.removeEventListener("input", onChangeHandler, {mozSystemGroup: true}); + this.form.rootElement.removeEventListener("reset", onChangeHandler, {mozSystemGroup: true}); } }; if (noFilledSectionsPreviously) { // Handle the highlight style resetting caused by user's correction afterward. log.debug("register change handler for filled form:", this.form); - this.form.rootElement.addEventListener("input", onChangeHandler); - this.form.rootElement.addEventListener("reset", onChangeHandler); + this.form.rootElement.addEventListener("input", onChangeHandler, {mozSystemGroup: true}); + this.form.rootElement.addEventListener("reset", onChangeHandler, {mozSystemGroup: true}); } } @@ -979,7 +979,7 @@ class FormAutofillHandler { if (!input) { continue; } - input.removeEventListener("input", this); + input.removeEventListener("input", this, {mozSystemGroup: true}); } this.timeStartedFillingMS = Date.now(); break; diff --git a/browser/modules/ContentLinkHandler.jsm b/browser/modules/ContentLinkHandler.jsm index 97c67cd05b28..5e889d6a1737 100644 --- a/browser/modules/ContentLinkHandler.jsm +++ b/browser/modules/ContentLinkHandler.jsm @@ -28,6 +28,9 @@ const SIZES_TELEMETRY_ENUM = { const FAVICON_PARSING_TIMEOUT = 100; const FAVICON_RICH_ICON_MIN_WIDTH = 96; +const TYPE_ICO = "image/x-icon"; +const TYPE_SVG = "image/svg+xml"; + /* * Create a nsITimer. * @@ -120,10 +123,27 @@ function setIconForLink(aIconInfo, aChromeGlobal) { } /** - * Checks whether the icon info represents an ICO image. + * Guess a type for an icon based on its declared type or file extension. */ -function isICO(icon) { - return icon.type == "image/x-icon" || icon.type == "image/vnd.microsoft.icon"; +function guessType(icon) { + // No type with no icon + if (!icon) { + return ""; + } + + // Use the file extension to guess at a type we're interested in + if (!icon.type) { + let extension = icon.iconUri.filePath.split(".").pop(); + switch (extension) { + case "ico": + return TYPE_ICO; + case "svg": + return TYPE_SVG; + } + } + + // Fuzzily prefer the type or fall back to the declared type + return icon.type == "image/vnd.microsoft.icon" ? TYPE_ICO : icon.type || ""; } /* @@ -142,9 +162,7 @@ function faviconTimeoutCallback(aFaviconLoads, aPageUrl, aChromeGlobal) { if (!load) return; - let preferredIcon = { - type: null - }; + let preferredIcon; let preferredWidth = 16 * Math.ceil(aChromeGlobal.content.devicePixelRatio); // Other links with the "icon" tag are the default icons let defaultIcon; @@ -157,11 +175,11 @@ function faviconTimeoutCallback(aFaviconLoads, aPageUrl, aChromeGlobal) { // First check for svg. If it's not available check for an icon with a // size adapt to the current resolution. If both are not available, prefer // ico files. When multiple icons are in the same set, the latest wins. - if (icon.type == "image/svg+xml") { + if (guessType(icon) == TYPE_SVG) { preferredIcon = icon; - } else if (icon.width == preferredWidth && preferredIcon.type != "image/svg+xml") { + } else if (icon.width == preferredWidth && guessType(preferredIcon) != TYPE_SVG) { preferredIcon = icon; - } else if (isICO(icon) && (preferredIcon.type == null || isICO(preferredIcon))) { + } else if (guessType(icon) == TYPE_ICO && (!preferredIcon || guessType(preferredIcon) == TYPE_ICO)) { preferredIcon = icon; } } @@ -185,7 +203,7 @@ function faviconTimeoutCallback(aFaviconLoads, aPageUrl, aChromeGlobal) { if (largestRichIcon) { setIconForLink(largestRichIcon, aChromeGlobal); } - if (preferredIcon.type) { + if (preferredIcon) { setIconForLink(preferredIcon, aChromeGlobal); } else if (defaultIcon) { setIconForLink(defaultIcon, aChromeGlobal); diff --git a/devtools/client/framework/devtools-browser.js b/devtools/client/framework/devtools-browser.js index 0d73a0b72803..286780a50e32 100644 --- a/devtools/client/framework/devtools-browser.js +++ b/devtools/client/framework/devtools-browser.js @@ -376,9 +376,8 @@ var gDevToolsBrowser = exports.gDevToolsBrowser = { if (processId) { return this._getContentProcessTarget(processId) .then(target => { - // Display a new toolbox, in a new window, with debugger by default - return gDevTools.showToolbox(target, "jsdebugger", - Toolbox.HostType.WINDOW); + // Display a new toolbox in a new window + return gDevTools.showToolbox(target, null, Toolbox.HostType.WINDOW); }); } diff --git a/dom/animation/test/css-animations/file_animations-dynamic-changes.html b/dom/animation/test/css-animations/file_animations-dynamic-changes.html index 8f16536ae98d..4075ad2978ad 100644 --- a/dom/animation/test/css-animations/file_animations-dynamic-changes.html +++ b/dom/animation/test/css-animations/file_animations-dynamic-changes.html @@ -26,7 +26,7 @@ promise_test(function(t) { // Wait a moment so we can confirm the startTime doesn't change (and // doesn't simply reflect the current time). - return waitForFrame(); + return waitForNextFrame(); }).then(function() { div.style.animationDuration = '200s'; var animation = div.getAnimations()[0]; diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp index adf0be83bcc5..f22fa97cc7af 100644 --- a/dom/base/CustomElementRegistry.cpp +++ b/dom/base/CustomElementRegistry.cpp @@ -18,6 +18,57 @@ namespace mozilla { namespace dom { +//----------------------------------------------------- +// CustomElementUpgradeReaction + +class CustomElementUpgradeReaction final : public CustomElementReaction +{ +public: + explicit CustomElementUpgradeReaction(CustomElementDefinition* aDefinition) + : mDefinition(aDefinition) + { +#if DEBUG + mIsUpgradeReaction = true; +#endif + } + +private: + virtual void Invoke(Element* aElement, ErrorResult& aRv) override + { + CustomElementRegistry::Upgrade(aElement, mDefinition, aRv); + } + + CustomElementDefinition* mDefinition; +}; + +//----------------------------------------------------- +// CustomElementCallbackReaction + +class CustomElementCallbackReaction final : public CustomElementReaction +{ + public: + explicit CustomElementCallbackReaction(UniquePtr aCustomElementCallback) + : mCustomElementCallback(Move(aCustomElementCallback)) + { + } + + virtual void Traverse(nsCycleCollectionTraversalCallback& aCb) const override + { + mCustomElementCallback->Traverse(aCb); + } + + private: + virtual void Invoke(Element* aElement, ErrorResult& aRv) override + { + mCustomElementCallback->Call(); + } + + UniquePtr mCustomElementCallback; +}; + +//----------------------------------------------------- +// CustomElementCallback + void CustomElementCallback::Call() { @@ -88,6 +139,7 @@ CustomElementConstructor::Construct(const char* aExecutionReason, return element.forget(); } + //----------------------------------------------------- // CustomElementData @@ -1120,24 +1172,5 @@ CustomElementDefinition::CustomElementDefinition(nsAtom* aType, { } - -//----------------------------------------------------- -// CustomElementUpgradeReaction - -/* virtual */ void -CustomElementUpgradeReaction::Invoke(Element* aElement, ErrorResult& aRv) -{ - CustomElementRegistry::Upgrade(aElement, mDefinition, aRv); -} - -//----------------------------------------------------- -// CustomElementCallbackReaction - -/* virtual */ void -CustomElementCallbackReaction::Invoke(Element* aElement, ErrorResult& aRv) -{ - mCustomElementCallback->Call(); -} - } // namespace dom } // namespace mozilla diff --git a/dom/base/CustomElementRegistry.h b/dom/base/CustomElementRegistry.h index a5aaa1305ffb..d91485fc1f6b 100644 --- a/dom/base/CustomElementRegistry.h +++ b/dom/base/CustomElementRegistry.h @@ -209,41 +209,6 @@ protected: #endif }; -class CustomElementUpgradeReaction final : public CustomElementReaction -{ -public: - explicit CustomElementUpgradeReaction(CustomElementDefinition* aDefinition) - : mDefinition(aDefinition) - { -#if DEBUG - mIsUpgradeReaction = true; -#endif - } - -private: - virtual void Invoke(Element* aElement, ErrorResult& aRv) override; - - CustomElementDefinition* mDefinition; -}; - -class CustomElementCallbackReaction final : public CustomElementReaction -{ - public: - explicit CustomElementCallbackReaction(UniquePtr aCustomElementCallback) - : mCustomElementCallback(Move(aCustomElementCallback)) - { - } - - virtual void Traverse(nsCycleCollectionTraversalCallback& aCb) const override - { - mCustomElementCallback->Traverse(aCb); - } - - private: - virtual void Invoke(Element* aElement, ErrorResult& aRv) override; - UniquePtr mCustomElementCallback; -}; - // https://html.spec.whatwg.org/multipage/scripting.html#custom-element-reactions-stack class CustomElementReactionsStack { diff --git a/dom/base/nsRange.cpp b/dom/base/nsRange.cpp index 29e742164b0a..02ac0772baf1 100644 --- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -200,9 +200,9 @@ nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset, NS_ASSERTION(n || !aNode->IsSelectionDescendant(), "orphan selection descendant"); - // Collect the potential ranges and their selection objects. - RangeHashTable ancestorSelectionRanges; + // Collect the selection objects for potential ranges. nsTHashtable> ancestorSelections; + Selection* prevSelection = nullptr; uint32_t maxRangeCount = 0; for (; n; n = GetNextRangeCommonAncestor(n->GetParentNode())) { LinkedList* ranges = n->GetExistingCommonAncestorRanges(); @@ -213,35 +213,64 @@ nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset, MOZ_ASSERT(range->IsInSelection(), "Why is this range registeed with a node?"); // Looks like that IsInSelection() assert fails sometimes... - if (!range->Collapsed() && range->IsInSelection()) { - ancestorSelectionRanges.PutEntry(range); + if (range->IsInSelection()) { Selection* selection = range->mSelection; - ancestorSelections.PutEntry(selection); + if (prevSelection != selection) { + prevSelection = selection; + ancestorSelections.PutEntry(selection); + } maxRangeCount = std::max(maxRangeCount, selection->RangeCount()); } } } - if (!ancestorSelectionRanges.IsEmpty()) { - nsTArray sortedRanges(maxRangeCount); + IsItemInRangeComparator comparator = { aNode, aStartOffset, aEndOffset }; + if (!ancestorSelections.IsEmpty()) { for (auto iter = ancestorSelections.ConstIter(); !iter.Done(); iter.Next()) { Selection* selection = iter.Get()->GetKey(); - // Sort the found ranges for |selection| in document order + // Binary search the sorted ranges in this selection. // (Selection::GetRangeAt returns its ranges ordered). - for (uint32_t i = 0, len = selection->RangeCount(); i < len; ++i) { - nsRange* range = selection->GetRangeAt(i); - if (ancestorSelectionRanges.Contains(range)) { - sortedRanges.AppendElement(range); + size_t low = 0; + size_t high = selection->RangeCount(); + + while (high != low) { + size_t middle = low + (high - low) / 2; + + const nsRange* const range = selection->GetRangeAt(middle); + int result = comparator(range); + if (result == 0) { + if (!range->Collapsed()) + return true; + + const nsRange* middlePlus1; + const nsRange* middleMinus1; + // if node end > start of middle+1, result = 1 + if (middle + 1 < high && + (middlePlus1 = selection->GetRangeAt(middle + 1)) && + nsContentUtils::ComparePoints( + aNode, static_cast(aEndOffset), + middlePlus1->GetStartContainer(), + static_cast(middlePlus1->StartOffset())) > 0) { + result = 1; + // if node start < end of middle - 1, result = -1 + } else if (middle >= 1 && + (middleMinus1 = selection->GetRangeAt(middle - 1)) && + nsContentUtils::ComparePoints( + aNode, static_cast(aStartOffset), + middleMinus1->GetEndContainer(), + static_cast(middleMinus1->EndOffset())) < 0) { + result = -1; + } else { + return false; + } + } + + if (result < 0) { + high = middle; + } else { + low = middle + 1; } } - MOZ_ASSERT(!sortedRanges.IsEmpty()); - // Binary search the now sorted ranges. - IsItemInRangeComparator comparator = { aNode, aStartOffset, aEndOffset }; - size_t unused; - if (mozilla::BinarySearchIf(sortedRanges, 0, sortedRanges.Length(), comparator, &unused)) { - return true; - } - sortedRanges.ClearAndRetainStorage(); } } return false; diff --git a/dom/encoding/FallbackEncoding.cpp b/dom/encoding/FallbackEncoding.cpp index ece9d9d20106..18df6ed84048 100644 --- a/dom/encoding/FallbackEncoding.cpp +++ b/dom/encoding/FallbackEncoding.cpp @@ -34,6 +34,7 @@ NS_IMPL_ISUPPORTS(FallbackEncoding, nsIObserver) FallbackEncoding* FallbackEncoding::sInstance = nullptr; bool FallbackEncoding::sGuessFallbackFromTopLevelDomain = true; +bool FallbackEncoding::sFallbackToUTF8ForFile = false; FallbackEncoding::FallbackEncoding() : mFallback(nullptr) @@ -139,6 +140,8 @@ FallbackEncoding::Initialize() nullptr); Preferences::AddBoolVarCache(&sGuessFallbackFromTopLevelDomain, "intl.charset.fallback.tld"); + Preferences::AddBoolVarCache(&sFallbackToUTF8ForFile, + "intl.charset.fallback.utf8_for_file"); nsCOMPtr obs = mozilla::services::GetObserverService(); if (obs) { diff --git a/dom/encoding/FallbackEncoding.h b/dom/encoding/FallbackEncoding.h index 355524699572..bca12bd8d1a8 100644 --- a/dom/encoding/FallbackEncoding.h +++ b/dom/encoding/FallbackEncoding.h @@ -26,6 +26,11 @@ public: */ static bool sGuessFallbackFromTopLevelDomain; + /** + * Whether UTF-8 should be used for file URLs. + */ + static bool sFallbackToUTF8ForFile; + /** * Gets the locale-dependent fallback encoding for legacy HTML and plain * text content. diff --git a/dom/html/nsHTMLDocument.cpp b/dom/html/nsHTMLDocument.cpp index effe2a27d8c7..5ad609c0802b 100644 --- a/dom/html/nsHTMLDocument.cpp +++ b/dom/html/nsHTMLDocument.cpp @@ -486,6 +486,12 @@ nsHTMLDocument::TryFallback(int32_t& aCharsetSource, return; aCharsetSource = kCharsetFromFallback; + bool isFile = false; + if (FallbackEncoding::sFallbackToUTF8ForFile && mDocumentURI && + NS_SUCCEEDED(mDocumentURI->SchemeIs("file", &isFile)) && isFile) { + aEncoding = UTF_8_ENCODING; + return; + } aEncoding = FallbackEncoding::FromLocale(); } diff --git a/dom/html/nsHTMLDocument.h b/dom/html/nsHTMLDocument.h index 6b58e57d68d8..e4f5becdd67d 100644 --- a/dom/html/nsHTMLDocument.h +++ b/dom/html/nsHTMLDocument.h @@ -362,8 +362,8 @@ protected: int32_t& charsetSource, NotNull& aEncoding); void TryTLD(int32_t& aCharsetSource, NotNull& aCharset); - static void TryFallback(int32_t& aCharsetSource, - NotNull& aEncoding); + void TryFallback(int32_t& aCharsetSource, + NotNull& aEncoding); // Override so we can munge the charset on our wyciwyg channel as needed. virtual void diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index f661e6df5b29..fe050b0123aa 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -1702,20 +1702,22 @@ MediaManager::EnumerateRawDevices(uint64_t aWindowId, } } + bool hasVideo = aVideoType != MediaSourceEnum::Other; + bool hasAudio = aAudioType != MediaSourceEnum::Other; + bool fakeCams = aFake && aVideoType == MediaSourceEnum::Camera; + bool fakeMics = aFake && aAudioType == MediaSourceEnum::Microphone; + bool realDevicesRequested = (!fakeCams && hasVideo) || (!fakeMics && hasAudio); + RefPtr task = NewTaskFrom([id, aWindowId, audioLoopDev, videoLoopDev, aVideoType, - aAudioType, aFake]() mutable { + aAudioType, hasVideo, hasAudio, + fakeCams, fakeMics, realDevicesRequested]() { // Only enumerate what's asked for, and only fake cams and mics. - bool hasVideo = aVideoType != MediaSourceEnum::Other; - bool hasAudio = aAudioType != MediaSourceEnum::Other; - bool fakeCams = aFake && aVideoType == MediaSourceEnum::Camera; - bool fakeMics = aFake && aAudioType == MediaSourceEnum::Microphone; - RefPtr fakeBackend, realBackend; if (fakeCams || fakeMics) { fakeBackend = new MediaEngineDefault(); } - if ((!fakeCams && hasVideo) || (!fakeMics && hasAudio)) { + if (realDevicesRequested) { MediaManager* manager = MediaManager::GetIfExists(); MOZ_RELEASE_ASSERT(manager); // Must exist while media thread is alive realBackend = manager->GetBackend(aWindowId); @@ -1758,9 +1760,7 @@ MediaManager::EnumerateRawDevices(uint64_t aWindowId, })); }); - if (!aFake && - (aVideoType == MediaSourceEnum::Camera || - aAudioType == MediaSourceEnum::Microphone) && + if (realDevicesRequested && Preferences::GetBool("media.navigator.permission.device", false)) { // Need to ask permission to retrieve list of all devices; // notify frontend observer and wait for callback notification to post task. @@ -2511,9 +2511,15 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, bool fake = c.mFake.WasPassed()? c.mFake.Value() : Preferences::GetBool("media.navigator.streams.fake"); + bool hasVideo = videoType != MediaSourceEnum::Other; + bool hasAudio = audioType != MediaSourceEnum::Other; + bool fakeCams = fake && videoType == MediaSourceEnum::Camera; + bool fakeMics = fake && audioType == MediaSourceEnum::Microphone; + bool realDevicesRequested = (!fakeCams && hasVideo) || (!fakeMics && hasAudio); + bool askPermission = (!privileged || Preferences::GetBool("media.navigator.permission.force")) && - (!fake || Preferences::GetBool("media.navigator.permission.fake")); + (realDevicesRequested || Preferences::GetBool("media.navigator.permission.fake")); RefPtr p = EnumerateDevicesImpl(windowID, videoType, audioType, fake); diff --git a/dom/media/webaudio/test/test_mediaElementAudioSourceNodeFidelity.html b/dom/media/webaudio/test/test_mediaElementAudioSourceNodeFidelity.html index f893eb19339a..7ad053123b24 100644 --- a/dom/media/webaudio/test/test_mediaElementAudioSourceNodeFidelity.html +++ b/dom/media/webaudio/test/test_mediaElementAudioSourceNodeFidelity.html @@ -46,23 +46,28 @@ function checkFrequency(an) { // We should have no energy when checking the data largely outside the index // for 440Hz (the frequency of the sine wave), start checking an octave above, // the Opus compression can add some harmonics to the pure since wave. - var index = binIndexForFrequency(880, an); - var underTreshold = true; - for (var i = index; i < frequencyArray.length; i++) { - // Let some slack, there might be some noise here because of int -> float - // conversion or the Opus encoding. - if (frequencyArray[i] > an.minDecibels + 40) { - return false; + var maxNoiseIndex = binIndexForFrequency(880, an); + for (var i = maxNoiseIndex + 1; i < frequencyArray.length; i++) { + if (frequencyArray[i] > frequencyArray[maxNoiseIndex]) { + maxNoiseIndex = i; } } // On the other hand, we should find a peak at 440Hz. Our sine wave is not // attenuated, we're expecting the peak to reach 0dBFs. - index = binIndexForFrequency(440, an); - info("energy at 440: " + frequencyArray[index] + ", threshold " + (an.maxDecibels - 10)); + var index = binIndexForFrequency(440, an); + info("energy at 440: " + frequencyArray[index] + + ", threshold " + (an.maxDecibels - 10) + + "; max noise at index " + maxNoiseIndex + + ": " + frequencyArray[maxNoiseIndex] ); if (frequencyArray[index] < (an.maxDecibels - 10)) { return false; } + // Let some slack, there might be some noise here because of int -> float + // conversion or the Opus encoding. + if (frequencyArray[maxNoiseIndex] > an.minDecibels + 40) { + return false; + } return true; } @@ -73,6 +78,10 @@ audioElement.loop = true; var ac = new AudioContext(); var mediaElementSource = ac.createMediaElementSource(audioElement); var an = ac.createAnalyser(); +// Use no smoothing as this would just average with previous +// getFloatFrequencyData() calls. Non-seamless looping would introduce noise, +// and smoothing would spread this into calls after the loop point. +an.smoothingTimeConstant = 0; frequencyArray = new Float32Array(an.frequencyBinCount); // Uncomment this to check what the analyser is doing. diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 07a4ff4f2398..36e05f61246f 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -907,7 +907,12 @@ nsEventStatus AsyncPanZoomController::HandleDragEvent(const MouseInput& aEvent, return nsEventStatus_eConsumeNoDefault; } + if (aEvent.mType == MouseInput::MouseType::MOUSE_DOWN) { + SetState(SCROLLBAR_DRAG); + } + if (aEvent.mType == MouseInput::MouseType::MOUSE_UP) { + SetState(NOTHING); ScrollSnap(); } @@ -1148,6 +1153,7 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent MOZ_ASSERT(GetCurrentTouchBlock()); GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CancelAnimations(ExcludeOverscroll); MOZ_FALLTHROUGH; + case SCROLLBAR_DRAG: case NOTHING: { mX.StartTouch(point.x, aEvent.mTime); mY.StartTouch(point.y, aEvent.mTime); @@ -1222,6 +1228,7 @@ nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent) case KEYBOARD_SCROLL: case OVERSCROLL_ANIMATION: case AUTOSCROLL: + case SCROLLBAR_DRAG: // Should not receive a touch-move in the OVERSCROLL_ANIMATION state // as touch blocks that begin in an overscrolled state cancel the // animation. The same is true for wheel scroll animations. @@ -1304,6 +1311,7 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) case KEYBOARD_SCROLL: case OVERSCROLL_ANIMATION: case AUTOSCROLL: + case SCROLLBAR_DRAG: // Should not receive a touch-end in the OVERSCROLL_ANIMATION state // as touch blocks that begin in an overscrolled state cancel the // animation. The same is true for WHEEL_SCROLL. diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index cb581e290e16..dc6fe79a4f43 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -928,7 +928,8 @@ protected: CSSOM-View smooth scroll-behavior */ WHEEL_SCROLL, /* Smooth scrolling to a destination for a wheel event. */ KEYBOARD_SCROLL, /* Smooth scrolling to a destination for a keyboard event. */ - AUTOSCROLL /* Autoscroll animation. */ + AUTOSCROLL, /* Autoscroll animation. */ + SCROLLBAR_DRAG /* Async scrollbar drag. */ }; // This is in theory protected by |mRecursiveMutex|; that is, it should be held whenever // this is updated. In practice though... see bug 897017. diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 93a7dcefc1fb..b30bb72d10ec 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -1132,13 +1132,6 @@ JS_EXPORT_API(void) DumpJSStack() xpc_DumpJSStack(true, true, false); } -JS_EXPORT_API(const char*) PrintJSStack() -{ - if (JSContext* cx = nsContentUtils::GetCurrentJSContext()) - return xpc_PrintJSStack(cx, true, true, false).release(); - return "There is no JSContext on the stack.\n"; -} - JS_EXPORT_API(void) DumpCompleteHeap() { nsCOMPtr listener = diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 030a1fc49cd8..4ec60b42f4dc 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -3650,8 +3650,8 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, CompositorHitTestInfo info = CompositorHitTestInfo::eVisibleToHitTest | CompositorHitTestInfo::eDispatchToContent; nsDisplayCompositorHitTestInfo* hitInfo = - new (aBuilder) nsDisplayCompositorHitTestInfo(aBuilder, mScrolledFrame, info, 1); - hitInfo->SetArea(mScrollPort + aBuilder->ToReferenceFrame(mOuter)); + new (aBuilder) nsDisplayCompositorHitTestInfo(aBuilder, mScrolledFrame, info, 1, + Some(mScrollPort + aBuilder->ToReferenceFrame(mOuter))); AppendInternalItemToTop(scrolledContent, hitInfo, zIndex); } if (aBuilder->IsBuildingLayerEventRegions()) { diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 6c3f41fd60a8..03d45c391476 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -4944,7 +4944,8 @@ nsDisplayEventReceiver::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& nsDisplayCompositorHitTestInfo::nsDisplayCompositorHitTestInfo(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, mozilla::gfx::CompositorHitTestInfo aHitTestInfo, - uint32_t aIndex) + uint32_t aIndex, + const mozilla::Maybe& aArea) : nsDisplayEventReceiver(aBuilder, aFrame) , mHitTestInfo(aHitTestInfo) , mIndex(aIndex) @@ -4962,12 +4963,29 @@ nsDisplayCompositorHitTestInfo::nsDisplayCompositorHitTestInfo(nsDisplayListBuil MOZ_ASSERT(mHitTestInfo & CompositorHitTestInfo::eScrollbar); mScrollTarget = Some(aBuilder->GetCurrentScrollbarTarget()); } -} -void -nsDisplayCompositorHitTestInfo::SetArea(const nsRect& aArea) -{ - mArea = Some(aArea); + if (aArea.isSome()) { + mArea = *aArea; + } else { + nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(mFrame); + if (scrollFrame) { + // If the frame is content of a scrollframe, then we need to pick up the + // area corresponding to the overflow rect as well. Otherwise the parts of + // the overflow that are not occupied by descendants get skipped and the + // APZ code sends touch events to the content underneath instead. + // See https://bugzilla.mozilla.org/show_bug.cgi?id=1127773#c15. + mArea = mFrame->GetScrollableOverflowRect(); + } else { + mArea = nsRect(nsPoint(0, 0), mFrame->GetSize()); + } + + // Note that it's important to do this call to ToReferenceFrame here in the + // nsDisplayCompositorHitTestInfo constructor, because then we'll hit the good + // fast path (because aBuilder will already have the info we want cached). + // This is as opposed to, say, calling it in CreateWebRenderCommands where + // we would not hit the fast path. + mArea += aBuilder->ToReferenceFrame(mFrame); + } } bool @@ -4977,31 +4995,13 @@ nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(mozilla::wr::DisplayList mozilla::layers::WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) { - if (mArea.isNothing()) { - nsRect borderBox; - nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(mFrame); - if (scrollFrame) { - // If the frame is content of a scrollframe, then we need to pick up the - // area corresponding to the overflow rect as well. Otherwise the parts of - // the overflow that are not occupied by descendants get skipped and the - // APZ code sends touch events to the content underneath instead. - // See https://bugzilla.mozilla.org/show_bug.cgi?id=1127773#c15. - borderBox = mFrame->GetScrollableOverflowRect(); - } else { - borderBox = nsRect(nsPoint(0, 0), mFrame->GetSize()); - } - - if (borderBox.IsEmpty()) { - return true; - } - - mArea = Some(borderBox + aDisplayListBuilder->ToReferenceFrame(mFrame)); + if (mArea.IsEmpty()) { + return true; } - MOZ_ASSERT(mArea.isSome()); wr::LayoutRect rect = aSc.ToRelativeLayoutRect( LayoutDeviceRect::FromAppUnits( - *mArea, + mArea, mFrame->PresContext()->AppUnitsPerDevPixel())); // XXX: eventually this scrollId computation and the SetHitTestInfo diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index 69f939680095..13653ee87f6a 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -4358,7 +4358,8 @@ class nsDisplayCompositorHitTestInfo : public nsDisplayEventReceiver { public: nsDisplayCompositorHitTestInfo(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, mozilla::gfx::CompositorHitTestInfo aHitTestInfo, - uint32_t aIndex = 0); + uint32_t aIndex = 0, + const mozilla::Maybe& aArea = mozilla::Nothing()); #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayCompositorHitTestInfo() @@ -4368,7 +4369,6 @@ public: #endif mozilla::gfx::CompositorHitTestInfo HitTestInfo() const { return mHitTestInfo; } - void SetArea(const nsRect& aArea); bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, @@ -4385,7 +4385,7 @@ public: private: mozilla::gfx::CompositorHitTestInfo mHitTestInfo; mozilla::Maybe mScrollTarget; - mozilla::Maybe mArea; + nsRect mArea; uint32_t mIndex; mozilla::Maybe mOverrideZIndex; }; diff --git a/layout/printing/DrawEventRecorder.cpp b/layout/printing/DrawEventRecorder.cpp index b552d747617d..9915527d02b1 100644 --- a/layout/printing/DrawEventRecorder.cpp +++ b/layout/printing/DrawEventRecorder.cpp @@ -19,12 +19,6 @@ DrawEventRecorderPRFileDesc::RecordEvent(const gfx::RecordedEvent& aEvent) Flush(); } -DrawEventRecorderPRFileDesc::DrawEventRecorderPRFileDesc(const char* aFilename) -{ - mOutputStream.Open(aFilename); - WriteHeader(mOutputStream); -} - DrawEventRecorderPRFileDesc::~DrawEventRecorderPRFileDesc() { if (IsOpen()) { @@ -45,11 +39,11 @@ DrawEventRecorderPRFileDesc::IsOpen() } void -DrawEventRecorderPRFileDesc::OpenNew(const char* aFilename) +DrawEventRecorderPRFileDesc::OpenFD(PRFileDesc* aFd) { MOZ_ASSERT(!IsOpen()); - mOutputStream.Open(aFilename); + mOutputStream.OpenFD(aFd); WriteHeader(mOutputStream); } diff --git a/layout/printing/DrawEventRecorder.h b/layout/printing/DrawEventRecorder.h index 3672c843d67f..fd46d28906ba 100644 --- a/layout/printing/DrawEventRecorder.h +++ b/layout/printing/DrawEventRecorder.h @@ -25,9 +25,10 @@ public: PRFileDescStream() : mFd(nullptr), mBuffer(nullptr), mBufferPos(0), mGood(true) {} - void Open(const char* aFilename) { + void OpenFD(PRFileDesc* aFd) + { MOZ_ASSERT(!IsOpen()); - mFd = PR_Open(aFilename, PR_RDWR | PR_CREATE_FILE, PR_IRUSR | PR_IWUSR); + mFd = aFd; mGood = true; mBuffer.reset(new uint8_t[kBufferSize]); mBufferPos = 0; @@ -118,7 +119,7 @@ class DrawEventRecorderPRFileDesc : public gfx::DrawEventRecorderPrivate { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPRFileDesc, override) - explicit DrawEventRecorderPRFileDesc(const char* aFilename); + explicit DrawEventRecorderPRFileDesc(){}; ~DrawEventRecorderPRFileDesc(); void RecordEvent(const gfx::RecordedEvent& aEvent) override; @@ -129,11 +130,9 @@ public: bool IsOpen(); /** - * Opens new file with the provided name. The recorder does NOT forget which - * objects it has recorded. This can be used with Close, so that a recording - * can be processed in chunks. The file must not already be open. + * Opens the recorder with the provided PRFileDesc *. */ - void OpenNew(const char* aFilename); + void OpenFD(PRFileDesc* aFd); /** * Closes the file so that it can be processed. The recorder does NOT forget diff --git a/layout/printing/ipc/PRemotePrintJob.ipdl b/layout/printing/ipc/PRemotePrintJob.ipdl index f6c1de89573a..9c9b6aa0c212 100644 --- a/layout/printing/ipc/PRemotePrintJob.ipdl +++ b/layout/printing/ipc/PRemotePrintJob.ipdl @@ -22,9 +22,9 @@ parent: async InitializePrint(nsString aDocumentTitle, nsString aPrintToFile, int32_t aStartPage, int32_t aEndPage); - // Translate the stored page recording and play back the events to the real - // print device. - async ProcessPage(nsCString aPageFileName); + // Translate the page recording writen into |fd| and play back the events to + // the real print device. + async ProcessPage(); // This informs the real print device that we've finished, so it can trigger // the actual print. @@ -45,11 +45,13 @@ parent: child: // Inform the child that the print has been initialized in the parent or has - // failed with result aRv. - async PrintInitializationResult(nsresult aRv); + // failed with result aRv. Includes a file descriptor which the first page + // can be written to. + async PrintInitializationResult(nsresult aRv, FileDescriptor aFd); - // Inform the child that the latest page has been processed remotely. - async PageProcessed(); + // Inform the child that the latest page has been processed remotely. Includes + // a file descriptor which the next page can be written to. + async PageProcessed(FileDescriptor aFd); async __delete__(); }; diff --git a/layout/printing/ipc/RemotePrintJobChild.cpp b/layout/printing/ipc/RemotePrintJobChild.cpp index 46cab20666c6..3d3dd5c33b59 100644 --- a/layout/printing/ipc/RemotePrintJobChild.cpp +++ b/layout/printing/ipc/RemotePrintJobChild.cpp @@ -9,6 +9,7 @@ #include "mozilla/Unused.h" #include "nsPagePrintTimer.h" #include "nsPrintEngine.h" +#include "private/pprio.h" namespace mozilla { namespace layout { @@ -36,26 +37,48 @@ RemotePrintJobChild::InitializePrint(const nsString& aDocumentTitle, } mozilla::ipc::IPCResult -RemotePrintJobChild::RecvPrintInitializationResult(const nsresult& aRv) +RemotePrintJobChild::RecvPrintInitializationResult( + const nsresult& aRv, + const mozilla::ipc::FileDescriptor& aFd) { mPrintInitialized = true; mInitializationResult = aRv; + if (NS_SUCCEEDED(aRv)) { + SetNextPageFD(aFd); + } return IPC_OK(); } +PRFileDesc* +RemotePrintJobChild::GetNextPageFD() +{ + MOZ_ASSERT(mNextPageFD); + PRFileDesc* fd = mNextPageFD; + mNextPageFD = nullptr; + return fd; +} + void -RemotePrintJobChild::ProcessPage(const nsCString& aPageFileName) +RemotePrintJobChild::SetNextPageFD(const mozilla::ipc::FileDescriptor& aFd) +{ + auto handle = aFd.ClonePlatformHandle(); + mNextPageFD = PR_ImportFile(PROsfd(handle.release())); +} + +void +RemotePrintJobChild::ProcessPage() { MOZ_ASSERT(mPagePrintTimer); mPagePrintTimer->WaitForRemotePrint(); - Unused << SendProcessPage(aPageFileName); + Unused << SendProcessPage(); } mozilla::ipc::IPCResult -RemotePrintJobChild::RecvPageProcessed() +RemotePrintJobChild::RecvPageProcessed(const mozilla::ipc::FileDescriptor& aFd) { MOZ_ASSERT(mPagePrintTimer); + SetNextPageFD(aFd); mPagePrintTimer->RemotePrintFinished(); return IPC_OK(); diff --git a/layout/printing/ipc/RemotePrintJobChild.h b/layout/printing/ipc/RemotePrintJobChild.h index 10bf8de9a26b..a45198c2cd56 100644 --- a/layout/printing/ipc/RemotePrintJobChild.h +++ b/layout/printing/ipc/RemotePrintJobChild.h @@ -34,11 +34,13 @@ public: const int32_t& aStartPage, const int32_t& aEndPage); - mozilla::ipc::IPCResult RecvPrintInitializationResult(const nsresult& aRv) final; + mozilla::ipc::IPCResult RecvPrintInitializationResult( + const nsresult& aRv, + const FileDescriptor& aFd) final; - void ProcessPage(const nsCString& aPageFileName); + void ProcessPage(); - mozilla::ipc::IPCResult RecvPageProcessed() final; + mozilla::ipc::IPCResult RecvPageProcessed(const FileDescriptor& aFd) final; mozilla::ipc::IPCResult RecvAbortPrint(const nsresult& aRv) final; @@ -46,13 +48,17 @@ public: void SetPrintEngine(nsPrintEngine* aPrintEngine); + PRFileDesc* GetNextPageFD(); + private: ~RemotePrintJobChild() final; + void SetNextPageFD(const mozilla::ipc::FileDescriptor& aFd); bool mPrintInitialized = false; nsresult mInitializationResult = NS_OK; RefPtr mPagePrintTimer; RefPtr mPrintEngine; + PRFileDesc* mNextPageFD = nullptr; }; } // namespace layout diff --git a/layout/printing/ipc/RemotePrintJobParent.cpp b/layout/printing/ipc/RemotePrintJobParent.cpp index 73a349302636..d28daf44493a 100644 --- a/layout/printing/ipc/RemotePrintJobParent.cpp +++ b/layout/printing/ipc/RemotePrintJobParent.cpp @@ -19,6 +19,8 @@ #include "nsIPrintSettings.h" #include "nsIWebProgressListener.h" #include "PrintTranslator.h" +#include "private/pprio.h" +#include "nsAnonymousTemporaryFile.h" namespace mozilla { namespace layout { @@ -38,14 +40,21 @@ RemotePrintJobParent::RecvInitializePrint(const nsString& aDocumentTitle, nsresult rv = InitializePrintDevice(aDocumentTitle, aPrintToFile, aStartPage, aEndPage); if (NS_FAILED(rv)) { - Unused << SendPrintInitializationResult(rv); + Unused << SendPrintInitializationResult(rv, FileDescriptor()); Unused << Send__delete__(this); return IPC_OK(); } mPrintTranslator.reset(new PrintTranslator(mPrintDeviceContext)); - Unused << SendPrintInitializationResult(NS_OK); + FileDescriptor fd; + rv = PrepareNextPageFD(&fd); + if (NS_FAILED(rv)) { + Unused << SendPrintInitializationResult(rv, FileDescriptor()); + Unused << Send__delete__(this); + return IPC_OK(); + } + Unused << SendPrintInitializationResult(NS_OK, fd); return IPC_OK(); } @@ -82,22 +91,49 @@ RemotePrintJobParent::InitializePrintDevice(const nsString& aDocumentTitle, return NS_OK; } -mozilla::ipc::IPCResult -RemotePrintJobParent::RecvProcessPage(const nsCString& aPageFileName) +nsresult +RemotePrintJobParent::PrepareNextPageFD(FileDescriptor* aFd) { - nsresult rv = PrintPage(aPageFileName); + PRFileDesc* prFd = nullptr; + nsresult rv = NS_OpenAnonymousTemporaryFile(&prFd); + if (NS_FAILED(rv)) { + return rv; + } + *aFd = FileDescriptor( + FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prFd))); + mCurrentPageStream.OpenFD(prFd); + return NS_OK; +} + +mozilla::ipc::IPCResult +RemotePrintJobParent::RecvProcessPage() +{ + if (!mCurrentPageStream.IsOpen()) { + Unused << SendAbortPrint(NS_ERROR_FAILURE); + return IPC_OK(); + } + mCurrentPageStream.Seek(0, PR_SEEK_SET); + nsresult rv = PrintPage(mCurrentPageStream); + mCurrentPageStream.Close(); if (NS_FAILED(rv)) { Unused << SendAbortPrint(rv); - } else { - Unused << SendPageProcessed(); + return IPC_OK(); } + FileDescriptor fd; + rv = PrepareNextPageFD(&fd); + if (NS_FAILED(rv)) { + Unused << SendAbortPrint(rv); + return IPC_OK(); + } + + Unused << SendPageProcessed(fd); return IPC_OK(); } nsresult -RemotePrintJobParent::PrintPage(const nsCString& aPageFileName) +RemotePrintJobParent::PrintPage(PRFileDescStream& aRecording) { MOZ_ASSERT(mPrintDeviceContext); @@ -105,29 +141,7 @@ RemotePrintJobParent::PrintPage(const nsCString& aPageFileName) if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - - nsCOMPtr recordingFile; - rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR, - getter_AddRefs(recordingFile)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = recordingFile->AppendNative(aPageFileName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsAutoCString recordingPath; - rv = recordingFile->GetNativePath(recordingPath); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - PRFileDescStream recording; - recording.Open(recordingPath.get()); - MOZ_ASSERT(recording.IsOpen()); - if (!mPrintTranslator->TranslateRecording(recording)) { + if (!mPrintTranslator->TranslateRecording(aRecording)) { return NS_ERROR_FAILURE; } @@ -136,12 +150,6 @@ RemotePrintJobParent::PrintPage(const nsCString& aPageFileName) return rv; } - recording.Close(); - rv = recordingFile->Remove(/* recursive= */ false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - return NS_OK; } diff --git a/layout/printing/ipc/RemotePrintJobParent.h b/layout/printing/ipc/RemotePrintJobParent.h index 0aa88bef1f55..6013374ac63b 100644 --- a/layout/printing/ipc/RemotePrintJobParent.h +++ b/layout/printing/ipc/RemotePrintJobParent.h @@ -8,11 +8,13 @@ #define mozilla_layout_RemotePrintJobParent_h #include "mozilla/layout/PRemotePrintJobParent.h" +#include "mozilla/layout/printing/DrawEventRecorder.h" #include "nsCOMArray.h" #include "nsCOMPtr.h" #include "mozilla/RefPtr.h" #include "mozilla/UniquePtr.h" +#include "mozilla/gfx/RecordedEvent.h" class nsDeviceContext; class nsIPrintSettings; @@ -34,7 +36,7 @@ public: const int32_t& aStartPage, const int32_t& aEndPage) final; - mozilla::ipc::IPCResult RecvProcessPage(const nsCString& aPageFileName) final; + mozilla::ipc::IPCResult RecvProcessPage() final; mozilla::ipc::IPCResult RecvFinalizePrint() final; @@ -70,12 +72,15 @@ private: const int32_t& aStartPage, const int32_t& aEndPage); - nsresult PrintPage(const nsCString& aPageFileName); + nsresult PrepareNextPageFD(FileDescriptor* aFd); + + nsresult PrintPage(PRFileDescStream& aRecording); nsCOMPtr mPrintSettings; RefPtr mPrintDeviceContext; UniquePtr mPrintTranslator; nsCOMArray mPrintProgressListeners; + PRFileDescStream mCurrentPageStream; }; } // namespace layout diff --git a/layout/printing/nsPagePrintTimer.cpp b/layout/printing/nsPagePrintTimer.cpp index 2a29bc85111c..eff9c78f82c6 100644 --- a/layout/printing/nsPagePrintTimer.cpp +++ b/layout/printing/nsPagePrintTimer.cpp @@ -145,7 +145,8 @@ nsPagePrintTimer::Notify(nsITimer *timer) if (mDocViewerPrint) { bool donePrePrint = true; - if (mPrintEngine) { + // Don't start to pre-print if we're waiting on the parent still. + if (mPrintEngine && !mWaitingForRemotePrint) { donePrePrint = mPrintEngine->PrePrintPage(); } diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp index e6e42dae4a40..0e3285430e53 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp @@ -50,13 +50,6 @@ static const char* pcmLogTag = "PeerConnectionMedia"; #endif #define LOGTAG pcmLogTag -//XXX(pkerr) What about bitrate settings? Going with the defaults for now. -RefPtr -CreateCall() -{ - return WebRtcCallWrapper::Create(); -} - NS_IMETHODIMP PeerConnectionMedia::ProtocolProxyQueryHandler:: OnProxyAvailable(nsICancelable *request, nsIChannel *aChannel, @@ -294,9 +287,6 @@ nsresult PeerConnectionMedia::Init(const std::vector& stun_serv } ConnectSignals(mIceCtxHdlr->ctx().get()); - // This webrtc:Call instance will be shared by audio and video media conduits. - mCall = CreateCall(); - return NS_OK; } @@ -1145,6 +1135,10 @@ PeerConnectionMedia::AddTransceiver( dom::MediaStreamTrack* aSendTrack, RefPtr* aTransceiverImpl) { + if (!mCall) { + mCall = WebRtcCallWrapper::Create(); + } + RefPtr transceiver = new TransceiverImpl( mParent->GetHandle(), aJsepTransceiver, diff --git a/mobile/android/app/src/main/res/values-v21/integers.xml b/mobile/android/app/src/main/res/values-v21/integers.xml new file mode 100644 index 000000000000..35d830569431 --- /dev/null +++ b/mobile/android/app/src/main/res/values-v21/integers.xml @@ -0,0 +1,10 @@ + + + + + + @drawable/icon + + diff --git a/mobile/android/app/src/main/res/values/integers.xml b/mobile/android/app/src/main/res/values/integers.xml index 867fdfacc201..9fbae2af5eb1 100644 --- a/mobile/android/app/src/main/res/values/integers.xml +++ b/mobile/android/app/src/main/res/values/integers.xml @@ -13,5 +13,6 @@ 2 2 500 + 0 diff --git a/mobile/android/base/AndroidManifest.xml.in b/mobile/android/base/AndroidManifest.xml.in index e365eda611e5..5801ce4da2f0 100644 --- a/mobile/android/base/AndroidManifest.xml.in +++ b/mobile/android/base/AndroidManifest.xml.in @@ -142,6 +142,22 @@ + + + + + + + + diff --git a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java index d7031ddab1a0..21bbe0d2703a 100644 --- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java @@ -435,7 +435,6 @@ public class BrowserApp extends GeckoApp case PAGE_SHOW: tab.loadFavicon(); break; - case UNSELECTED: // We receive UNSELECTED immediately after the SELECTED listeners run // so we are ensured that the unselectedTabEditingText has not changed. @@ -444,6 +443,9 @@ public class BrowserApp extends GeckoApp tab.getEditingState().copyFrom(mLastTabEditingState); } break; + case START_EDITING: + enterEditingMode(); + break; } if (HardwareUtils.isTablet() && msg == TabEvents.SELECTED) { diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java index 376a864f7ec7..b4dd07afd110 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java @@ -1411,8 +1411,10 @@ public abstract class GeckoApp extends GeckoActivity final String passedUri = getIntentURI(intent); - final boolean isExternalURL = passedUri != null; - final boolean isAboutHomeURL = isExternalURL && AboutPages.isAboutHome(passedUri); + final boolean intentHasURL = passedUri != null; + final boolean isAboutHomeURL = intentHasURL && AboutPages.isAboutHome(passedUri); + final boolean isAssistIntent = Intent.ACTION_ASSIST.equals(action); + final boolean needsNewForegroundTab = intentHasURL || isAssistIntent; // Start migrating as early as possible, can do this in // parallel with Gecko load. @@ -1438,14 +1440,16 @@ public abstract class GeckoApp extends GeckoActivity handleSelectTabIntent(intent); // External URLs and new tab from widget should always be loaded regardless of whether Gecko is // already running. - } else if (isExternalURL) { + } else if (needsNewForegroundTab) { // Restore tabs before opening an external URL so that the new tab // is animated properly. Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED); processActionViewIntent(new Runnable() { @Override public void run() { - if (isAboutHomeURL) { + if (isAssistIntent) { + Tabs.getInstance().addTab(Tabs.LOADURL_START_EDITING | Tabs.LOADURL_EXTERNAL); + } else if (isAboutHomeURL) { // respect the user preferences for about:home from external intent calls loadStartupTab(Tabs.LOADURL_NEW_TAB, action); } else { @@ -1780,6 +1784,8 @@ public abstract class GeckoApp extends GeckoActivity Tabs.getInstance().loadUrlWithIntentExtras(url, intent, flags); } }); + } else if (Intent.ACTION_ASSIST.equals(action)) { + Tabs.getInstance().addTab(Tabs.LOADURL_START_EDITING | Tabs.LOADURL_EXTERNAL); } else if (ACTION_HOMESCREEN_SHORTCUT.equals(action)) { final GeckoBundle data = new GeckoBundle(2); data.putString("uri", uri); diff --git a/mobile/android/base/java/org/mozilla/gecko/Tabs.java b/mobile/android/base/java/org/mozilla/gecko/Tabs.java index d167c0c3b89e..9607b2d63093 100644 --- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java +++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java @@ -84,6 +84,8 @@ public class Tabs implements BundleEventListener { public static final int LOADURL_EXTERNAL = 1 << 7; /** Indicates the tab is the first shown after Firefox is hidden and restored. */ public static final int LOADURL_FIRST_AFTER_ACTIVITY_UNHIDDEN = 1 << 8; + /** Indicates that we should enter editing mode after opening the tab. */ + public static final int LOADURL_START_EDITING = 1 << 9; private static final long PERSIST_TABS_AFTER_MILLISECONDS = 1000 * 2; @@ -758,7 +760,8 @@ public class Tabs implements BundleEventListener { AUDIO_PLAYING_CHANGE, OPENED_FROM_TABS_TRAY, MEDIA_PLAYING_CHANGE, - MEDIA_PLAYING_RESUME + MEDIA_PLAYING_RESUME, + START_EDITING, } public void notifyListeners(Tab tab, TabEvents msg) { @@ -987,6 +990,7 @@ public class Tabs implements BundleEventListener { boolean desktopMode = (flags & LOADURL_DESKTOP) != 0; boolean external = (flags & LOADURL_EXTERNAL) != 0; final boolean isFirstShownAfterActivityUnhidden = (flags & LOADURL_FIRST_AFTER_ACTIVITY_UNHIDDEN) != 0; + final boolean startEditing = (flags & LOADURL_START_EDITING) != 0; data.putString("url", url); data.putString("engine", searchEngine); @@ -1065,26 +1069,40 @@ public class Tabs implements BundleEventListener { selectTab(tabToSelect.getId()); } + if (startEditing) { + notifyListeners(tabToSelect, TabEvents.START_EDITING); + } + // Load favicon instantly for about:home page because it's already cached if (AboutPages.isBuiltinIconPage(url)) { tabToSelect.loadFavicon(); } - return tabToSelect; } /** - * Opens a new tab and loads either about:home or, if PREFS_HOMEPAGE_FOR_EVERY_NEW_TAB is set, - * the user's homepage. + * Opens a new tab and loads a page according to the user's preferences (by default about:home). */ @RobocopTarget public Tab addTab() { - return loadUrl(getHomepageForNewTab(mAppContext), Tabs.LOADURL_NEW_TAB); + return addTab(Tabs.LOADURL_NONE); } + /** + * Opens a new tab and loads a page according to the user's preferences (by default about:home). + */ public Tab addPrivateTab() { - return loadUrl(getHomepageForNewTab(mAppContext), Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_PRIVATE); + return addTab(Tabs.LOADURL_PRIVATE); + } + + /** + * Opens a new tab and loads a page according to the user's preferences (by default about:home). + * + * @param flags additional flags used when opening the tab + */ + public Tab addTab(int flags) { + return loadUrl(getHomepageForNewTab(mAppContext), flags | Tabs.LOADURL_NEW_TAB); } /** diff --git a/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictionProvider.java b/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictionProvider.java index 26b9a446f2f9..e7faa3a89be5 100644 --- a/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictionProvider.java +++ b/mobile/android/base/java/org/mozilla/gecko/restrictions/RestrictionProvider.java @@ -38,12 +38,12 @@ public class RestrictionProvider extends BroadcastReceiver { @Override public void run() { final Bundle oldRestrictions = intent.getBundleExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE); - RestrictionCache.migrateRestrictionsIfNeeded(oldRestrictions); - final Bundle extras = new Bundle(); - - ArrayList entries = initRestrictions(context, oldRestrictions); - extras.putParcelableArrayList(Intent.EXTRA_RESTRICTIONS_LIST, entries); + if (oldRestrictions != null) { + RestrictionCache.migrateRestrictionsIfNeeded(oldRestrictions); + ArrayList entries = initRestrictions(context, oldRestrictions); + extras.putParcelableArrayList(Intent.EXTRA_RESTRICTIONS_LIST, entries); + } result.setResult(Activity.RESULT_OK, null, extras); result.finish(); diff --git a/mobile/android/components/NSSDialogService.js b/mobile/android/components/NSSDialogService.js index f71a617659cb..9e0f8e777ea1 100644 --- a/mobile/android/components/NSSDialogService.js +++ b/mobile/android/components/NSSDialogService.js @@ -92,8 +92,7 @@ NSSDialogs.prototype = { ], aCtx); prompt.addCheckbox({ id: "trustSSL", label: this.getString("downloadCert.trustSSL"), checked: false }) - .addCheckbox({ id: "trustEmail", label: this.getString("downloadCert.trustEmail"), checked: false }) - .addCheckbox({ id: "trustSign", label: this.getString("downloadCert.trustObjSign"), checked: false }); + .addCheckbox({ id: "trustEmail", label: this.getString("downloadCert.trustEmail"), checked: false }); let response = this.showPrompt(prompt); // they hit the "view cert" button, so show the cert and try again @@ -107,7 +106,6 @@ NSSDialogs.prototype = { aTrust.value = Ci.nsIX509CertDB.UNTRUSTED; if (response.trustSSL) aTrust.value |= Ci.nsIX509CertDB.TRUSTED_SSL; if (response.trustEmail) aTrust.value |= Ci.nsIX509CertDB.TRUSTED_EMAIL; - if (response.trustSign) aTrust.value |= Ci.nsIX509CertDB.TRUSTED_OBJSIGN; return true; } }, diff --git a/mobile/android/locales/en-US/chrome/pippki.properties b/mobile/android/locales/en-US/chrome/pippki.properties index 1102f0b00a3f..edd8a890f7e8 100644 --- a/mobile/android/locales/en-US/chrome/pippki.properties +++ b/mobile/android/locales/en-US/chrome/pippki.properties @@ -11,7 +11,6 @@ downloadCert.message1=You have been asked to trust a new Certificate Authority ( downloadCert.viewCert.label=View downloadCert.trustSSL=Trust to identify websites. downloadCert.trustEmail=Trust to identify email users. -downloadCert.trustObjSign=Trust to identify software developers. pkcs12.getpassword.title=Password Entry Dialog pkcs12.getpassword.message=Please enter the password that was used to encrypt this certificate backup. clientAuthAsk.title=User Identification Request diff --git a/mobile/android/tests/browser/robocop/robocop.ini b/mobile/android/tests/browser/robocop/robocop.ini index d9946dd77b2e..f5a478748d18 100644 --- a/mobile/android/tests/browser/robocop/robocop.ini +++ b/mobile/android/tests/browser/robocop/robocop.ini @@ -11,6 +11,8 @@ skip-if = android_version == "18" # disabled on 4.3, bug 1120759 skip-if = android_version == "18" [src/org/mozilla/gecko/tests/testANRReporter.java] +[src/org/mozilla/gecko/tests/testAssistIntentNewIntent.java] +[src/org/mozilla/gecko/tests/testAssistIntentStartup.java] [src/org/mozilla/gecko/tests/testAudioFocus.java] [src/org/mozilla/gecko/tests/testMediaControl.java] skip-if = android_version < "23" diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/AssistIntentTest.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/AssistIntentTest.java new file mode 100644 index 000000000000..d86354cd015f --- /dev/null +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/AssistIntentTest.java @@ -0,0 +1,29 @@ +package org.mozilla.gecko.tests; + +import android.content.Intent; + +import org.mozilla.gecko.tests.helpers.GeckoHelper; + +public class AssistIntentTest extends SessionTest { + @Override + public void setActivityIntent(Intent intent) { + // We want to make sure that we have opened a new tab, so we're creating a session + // where the default selected tab will *not* be about:home. + Session session = createTestSession(/*selected tab*/ 1); + injectSessionToRestore(intent, session); + + super.setActivityIntent(intent); + } + + protected void verifyAssistIntentHandling() { + // After an ACTION_ASSIST intent, we should be in editing mode... + mToolbar.assertIsEditing(); + + // ... the input field should be empty in readiness for the user to type... + mToolbar.assertUrl(""); + + // ... and we should have opened a new tab in addition to those from the "restored" session. + mToolbar.dismissEditingMode(); + mToolbar.assertTitle(mStringHelper.ABOUT_HOME_URL); + } +} diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/ToolbarComponent.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/ToolbarComponent.java index d4c226e9128d..2c5f559d83b9 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/ToolbarComponent.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/ToolbarComponent.java @@ -75,7 +75,7 @@ public class ToolbarComponent extends BaseComponent { public ToolbarComponent assertUrl(final String expected) { assertIsEditing(); - fAssertEquals("The Toolbar url is " + expected, expected, getUrlEditText().getText()); + fAssertEquals("The Toolbar url is " + expected, expected, getUrlEditText().getText().toString()); return this; } diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAssistIntentNewIntent.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAssistIntentNewIntent.java new file mode 100644 index 000000000000..c3740f022849 --- /dev/null +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAssistIntentNewIntent.java @@ -0,0 +1,28 @@ +package org.mozilla.gecko.tests; + +import android.content.Context; +import android.content.Intent; + +import org.mozilla.gecko.AppConstants; +import org.mozilla.gecko.tests.helpers.GeckoHelper; +import org.mozilla.gecko.tests.helpers.WaitHelper; + +public class testAssistIntentNewIntent extends AssistIntentTest { + public void testAssistIntentNewIntent() { + GeckoHelper.blockForReady(); + + final Context testContext = getInstrumentation().getContext(); + final Intent assistIntent = new Intent(Intent.ACTION_ASSIST); + assistIntent.setPackage(AppConstants.ANDROID_PACKAGE_NAME); + assistIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + WaitHelper.waitForPageLoad(new Runnable() { + @Override + public void run() { + testContext.startActivity(assistIntent); + } + }); + + verifyAssistIntentHandling(); + } +} diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAssistIntentStartup.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAssistIntentStartup.java new file mode 100644 index 000000000000..cbcd27b5e3fd --- /dev/null +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAssistIntentStartup.java @@ -0,0 +1,20 @@ +package org.mozilla.gecko.tests; + +import android.content.Intent; + +import org.mozilla.gecko.tests.helpers.GeckoHelper; + +public class testAssistIntentStartup extends AssistIntentTest { + @Override + public void setActivityIntent(Intent intent) { + intent.setAction(Intent.ACTION_ASSIST); + + super.setActivityIntent(intent); + } + + public void testAssistIntentStartup() { + GeckoHelper.blockForReady(); + + verifyAssistIntentHandling(); + } +} diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 6952304dc101..271910a6ea09 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2291,6 +2291,7 @@ pref("intl.menuitems.insertseparatorbeforeaccesskeys","chrome://global/locale/in pref("intl.charset.detector", "chrome://global/locale/intl.properties"); pref("intl.charset.fallback.override", ""); pref("intl.charset.fallback.tld", true); +pref("intl.charset.fallback.utf8_for_file", false); pref("intl.ellipsis", "chrome://global-platform/locale/intl.properties"); pref("intl.locale.matchOS", false); // this pref allows user to request that all internationalization formatters diff --git a/security/manager/locales/en-US/chrome/pippki/certManager.dtd b/security/manager/locales/en-US/chrome/pippki/certManager.dtd index 880321cd444f..e6318e1ce096 100644 --- a/security/manager/locales/en-US/chrome/pippki/certManager.dtd +++ b/security/manager/locales/en-US/chrome/pippki/certManager.dtd @@ -38,7 +38,6 @@ - diff --git a/security/manager/locales/en-US/chrome/pippki/pippki.dtd b/security/manager/locales/en-US/chrome/pippki/pippki.dtd index 3bc3c1f85957..00e1f6a30a4e 100644 --- a/security/manager/locales/en-US/chrome/pippki/pippki.dtd +++ b/security/manager/locales/en-US/chrome/pippki/pippki.dtd @@ -20,7 +20,6 @@ - diff --git a/security/manager/pki/nsNSSDialogs.cpp b/security/manager/pki/nsNSSDialogs.cpp index 8e41d4f778ad..fcba770c1d97 100644 --- a/security/manager/pki/nsNSSDialogs.cpp +++ b/security/manager/pki/nsNSSDialogs.cpp @@ -153,16 +153,9 @@ nsNSSDialogs::ConfirmDownloadCACert(nsIInterfaceRequestor* ctx, if (NS_FAILED(rv)) { return rv; } - bool trustForObjSign = false; - rv = retVals->GetPropertyAsBool(NS_LITERAL_STRING("trustForObjSign"), - &trustForObjSign); - if (NS_FAILED(rv)) { - return rv; - } *trust |= trustForSSL ? nsIX509CertDB::TRUSTED_SSL : 0; *trust |= trustForEmail ? nsIX509CertDB::TRUSTED_EMAIL : 0; - *trust |= trustForObjSign ? nsIX509CertDB::TRUSTED_OBJSIGN : 0; return NS_OK; } diff --git a/security/manager/pki/resources/content/downloadcert.js b/security/manager/pki/resources/content/downloadcert.js index 892e34be081c..c5cda01a1831 100644 --- a/security/manager/pki/resources/content/downloadcert.js +++ b/security/manager/pki/resources/content/downloadcert.js @@ -27,9 +27,6 @@ * @property {Boolean} trustForEmail * Set to true if the cert should be trusted for e-mail, false * otherwise. Undefined value if |importConfirmed| is not true. - * @property {Boolean} trustForObjSign - * Set to true if the cert should be trusted for object signing, false - * otherwise. Undefined value if |importConfirmed| is not true. */ const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; @@ -70,13 +67,11 @@ function viewCert() { function onDialogAccept() { let checkSSL = document.getElementById("trustSSL"); let checkEmail = document.getElementById("trustEmail"); - let checkObjSign = document.getElementById("trustObjSign"); let retVals = window.arguments[1].QueryInterface(Ci.nsIWritablePropertyBag2); retVals.setPropertyAsBool("importConfirmed", true); retVals.setPropertyAsBool("trustForSSL", checkSSL.checked); retVals.setPropertyAsBool("trustForEmail", checkEmail.checked); - retVals.setPropertyAsBool("trustForObjSign", checkObjSign.checked); return true; } diff --git a/security/manager/pki/resources/content/downloadcert.xul b/security/manager/pki/resources/content/downloadcert.xul index 9ddc88a17ce8..7e19de2c88d0 100644 --- a/security/manager/pki/resources/content/downloadcert.xul +++ b/security/manager/pki/resources/content/downloadcert.xul @@ -33,7 +33,6 @@ - "do you want to?" - * trust for SSL - * trust for email - - * trust for object signing --> @@ -41,8 +40,6 @@ id="trustSSL"/> - diff --git a/security/manager/pki/resources/content/editcacert.js b/security/manager/pki/resources/content/editcacert.js index 405d5281a42c..1414ac653cac 100644 --- a/security/manager/pki/resources/content/editcacert.js +++ b/security/manager/pki/resources/content/editcacert.js @@ -31,11 +31,6 @@ function onLoad() { let emailCheckbox = document.getElementById("trustEmail"); emailCheckbox.checked = gCertDB.isCertTrusted(gCert, Ci.nsIX509Cert.CA_CERT, Ci.nsIX509CertDB.TRUSTED_EMAIL); - - let objSignCheckbox = document.getElementById("trustObjSign"); - objSignCheckbox.checked = - gCertDB.isCertTrusted(gCert, Ci.nsIX509Cert.CA_CERT, - Ci.nsIX509CertDB.TRUSTED_OBJSIGN); } /** @@ -46,13 +41,9 @@ function onLoad() { function onDialogAccept() { let sslCheckbox = document.getElementById("trustSSL"); let emailCheckbox = document.getElementById("trustEmail"); - let objSignCheckbox = document.getElementById("trustObjSign"); let trustSSL = sslCheckbox.checked ? Ci.nsIX509CertDB.TRUSTED_SSL : 0; let trustEmail = emailCheckbox.checked ? Ci.nsIX509CertDB.TRUSTED_EMAIL : 0; - let trustObjSign = objSignCheckbox.checked ? Ci.nsIX509CertDB.TRUSTED_OBJSIGN - : 0; - gCertDB.setCertTrust(gCert, Ci.nsIX509Cert.CA_CERT, - trustSSL | trustEmail | trustObjSign); + gCertDB.setCertTrust(gCert, Ci.nsIX509Cert.CA_CERT, trustSSL | trustEmail); return true; } diff --git a/security/manager/pki/resources/content/editcacert.xul b/security/manager/pki/resources/content/editcacert.xul index 46ea4f1f928d..0cba6d6d3cb6 100644 --- a/security/manager/pki/resources/content/editcacert.xul +++ b/security/manager/pki/resources/content/editcacert.xul @@ -29,8 +29,6 @@ id="trustSSL"/> - diff --git a/security/manager/ssl/nsIX509CertDB.idl b/security/manager/ssl/nsIX509CertDB.idl index d3c795c03c84..d7983be77230 100644 --- a/security/manager/ssl/nsIX509CertDB.idl +++ b/security/manager/ssl/nsIX509CertDB.idl @@ -67,7 +67,6 @@ interface nsIX509CertDB : nsISupports { const unsigned long UNTRUSTED = 0; const unsigned long TRUSTED_SSL = 1 << 0; const unsigned long TRUSTED_EMAIL = 1 << 1; - const unsigned long TRUSTED_OBJSIGN = 1 << 2; /** * Will find a certificate based on its dbkey @@ -155,8 +154,11 @@ interface nsIX509CertDB : nsISupports { /** * @param cert The certificate for which to modify trust. * @param trustString decoded by CERT_DecodeTrustString. 3 comma separated - * characters, indicating SSL, Email, and Obj signing - * trust. + * characters, indicating SSL, Email, and Object signing + * trust. The object signing trust flags are effectively + * ignored by gecko, but they still must be specified (at + * least by a final trailing comma) because this argument + * is passed to CERT_DecodeTrustString. */ [must_use] void setCertTrustFromString(in nsIX509Cert cert, in ACString trustString); @@ -284,7 +286,10 @@ interface nsIX509CertDB : nsISupports { * @param trust String describing the trust settings to assign the * certificate. Decoded by CERT_DecodeTrustString. Consists of 3 * comma separated sets of characters, indicating SSL, Email, and - * Object signing trust. + * Object signing trust. The object signing trust flags are + * effectively ignored by gecko, but they still must be specified + * (at least by a final trailing comma) because this argument is + * passed to CERT_DecodeTrustString. * @return nsIX509Cert the resulting certificate */ [must_use] @@ -359,7 +364,10 @@ interface nsIX509CertDB : nsISupports { * @param trust String describing the trust settings to assign the * certificate. Decoded by CERT_DecodeTrustString. Consists of 3 * comma separated sets of characters, indicating SSL, Email, and - * Object signing trust. + * Object signing trust. The object signing trust flags are + * effectively ignored by gecko, but they still must be specified + * (at least by a final trailing comma) because this argument is + * passed to CERT_DecodeTrustString. * @return nsIX509Cert the resulting certificate */ [must_use] diff --git a/security/manager/ssl/nsNSSCertHelper.cpp b/security/manager/ssl/nsNSSCertHelper.cpp index 1ede4d242577..4735606bbdc5 100644 --- a/security/manager/ssl/nsNSSCertHelper.cpp +++ b/security/manager/ssl/nsNSSCertHelper.cpp @@ -2039,9 +2039,9 @@ getCertType(CERTCertificate* cert) return nsIX509Cert::USER_CERT; if (trust.HasAnyCA()) return nsIX509Cert::CA_CERT; - if (trust.HasPeer(true, false, false)) + if (trust.HasPeer(true, false)) return nsIX509Cert::SERVER_CERT; - if (trust.HasPeer(false, true, false) && cert->emailAddr) + if (trust.HasPeer(false, true) && cert->emailAddr) return nsIX509Cert::EMAIL_CERT; if (CERT_IsCACert(cert, nullptr)) return nsIX509Cert::CA_CERT; diff --git a/security/manager/ssl/nsNSSCertTrust.cpp b/security/manager/ssl/nsNSSCertTrust.cpp index d7dd266691b7..46dd005f201d 100644 --- a/security/manager/ssl/nsNSSCertTrust.cpp +++ b/security/manager/ssl/nsNSSCertTrust.cpp @@ -5,7 +5,7 @@ #include "nsNSSCertTrust.h" void -nsNSSCertTrust::AddCATrust(bool ssl, bool email, bool objSign) +nsNSSCertTrust::AddCATrust(bool ssl, bool email) { if (ssl) { addTrust(&mTrust.sslFlags, CERTDB_TRUSTED_CA); @@ -15,21 +15,15 @@ nsNSSCertTrust::AddCATrust(bool ssl, bool email, bool objSign) addTrust(&mTrust.emailFlags, CERTDB_TRUSTED_CA); addTrust(&mTrust.emailFlags, CERTDB_TRUSTED_CLIENT_CA); } - if (objSign) { - addTrust(&mTrust.objectSigningFlags, CERTDB_TRUSTED_CA); - addTrust(&mTrust.objectSigningFlags, CERTDB_TRUSTED_CLIENT_CA); - } } void -nsNSSCertTrust::AddPeerTrust(bool ssl, bool email, bool objSign) +nsNSSCertTrust::AddPeerTrust(bool ssl, bool email) { if (ssl) addTrust(&mTrust.sslFlags, CERTDB_TRUSTED); if (email) addTrust(&mTrust.emailFlags, CERTDB_TRUSTED); - if (objSign) - addTrust(&mTrust.objectSigningFlags, CERTDB_TRUSTED); } nsNSSCertTrust::nsNSSCertTrust() @@ -37,14 +31,11 @@ nsNSSCertTrust::nsNSSCertTrust() memset(&mTrust, 0, sizeof(CERTCertTrust)); } -nsNSSCertTrust::nsNSSCertTrust(unsigned int ssl, - unsigned int email, - unsigned int objsign) +nsNSSCertTrust::nsNSSCertTrust(unsigned int ssl, unsigned int email) { memset(&mTrust, 0, sizeof(CERTCertTrust)); addTrust(&mTrust.sslFlags, ssl); addTrust(&mTrust.emailFlags, email); - addTrust(&mTrust.objectSigningFlags, objsign); } nsNSSCertTrust::nsNSSCertTrust(CERTCertTrust *t) @@ -103,28 +94,6 @@ nsNSSCertTrust::SetEmailTrust(bool peer, bool tPeer, addTrust(&mTrust.emailFlags, CERTDB_SEND_WARN); } -void -nsNSSCertTrust::SetObjSignTrust(bool peer, bool tPeer, - bool ca, bool tCA, bool tClientCA, - bool user, bool warn) -{ - mTrust.objectSigningFlags = 0; - if (peer || tPeer) - addTrust(&mTrust.objectSigningFlags, CERTDB_TERMINAL_RECORD); - if (tPeer) - addTrust(&mTrust.objectSigningFlags, CERTDB_TRUSTED); - if (ca || tCA) - addTrust(&mTrust.objectSigningFlags, CERTDB_VALID_CA); - if (tClientCA) - addTrust(&mTrust.objectSigningFlags, CERTDB_TRUSTED_CLIENT_CA); - if (tCA) - addTrust(&mTrust.objectSigningFlags, CERTDB_TRUSTED_CA); - if (user) - addTrust(&mTrust.objectSigningFlags, CERTDB_USER); - if (warn) - addTrust(&mTrust.objectSigningFlags, CERTDB_SEND_WARN); -} - void nsNSSCertTrust::SetValidCA() { @@ -134,9 +103,6 @@ nsNSSCertTrust::SetValidCA() SetEmailTrust(false, false, true, false, false, false, false); - SetObjSignTrust(false, false, - true, false, false, - false, false); } void @@ -148,9 +114,6 @@ nsNSSCertTrust::SetValidPeer() SetEmailTrust(true, false, false, false, false, false, false); - SetObjSignTrust(true, false, - false, false, false, - false, false); } bool @@ -164,16 +127,12 @@ nsNSSCertTrust::HasAnyCA() } bool -nsNSSCertTrust::HasPeer(bool checkSSL, - bool checkEmail, - bool checkObjSign) +nsNSSCertTrust::HasPeer(bool checkSSL, bool checkEmail) { if (checkSSL && !hasTrust(mTrust.sslFlags, CERTDB_TERMINAL_RECORD)) return false; if (checkEmail && !hasTrust(mTrust.emailFlags, CERTDB_TERMINAL_RECORD)) return false; - if (checkObjSign && !hasTrust(mTrust.objectSigningFlags, CERTDB_TERMINAL_RECORD)) - return false; return true; } @@ -188,9 +147,7 @@ nsNSSCertTrust::HasAnyUser() } bool -nsNSSCertTrust::HasTrustedCA(bool checkSSL, - bool checkEmail, - bool checkObjSign) +nsNSSCertTrust::HasTrustedCA(bool checkSSL, bool checkEmail) { if (checkSSL && !(hasTrust(mTrust.sslFlags, CERTDB_TRUSTED_CA) || hasTrust(mTrust.sslFlags, CERTDB_TRUSTED_CLIENT_CA))) @@ -198,25 +155,16 @@ nsNSSCertTrust::HasTrustedCA(bool checkSSL, if (checkEmail && !(hasTrust(mTrust.emailFlags, CERTDB_TRUSTED_CA) || hasTrust(mTrust.emailFlags, CERTDB_TRUSTED_CLIENT_CA))) return false; - if (checkObjSign && - !(hasTrust(mTrust.objectSigningFlags, CERTDB_TRUSTED_CA) || - hasTrust(mTrust.objectSigningFlags, CERTDB_TRUSTED_CLIENT_CA))) - return false; return true; } bool -nsNSSCertTrust::HasTrustedPeer(bool checkSSL, - bool checkEmail, - bool checkObjSign) +nsNSSCertTrust::HasTrustedPeer(bool checkSSL, bool checkEmail) { if (checkSSL && !(hasTrust(mTrust.sslFlags, CERTDB_TRUSTED))) return false; if (checkEmail && !(hasTrust(mTrust.emailFlags, CERTDB_TRUSTED))) return false; - if (checkObjSign && - !(hasTrust(mTrust.objectSigningFlags, CERTDB_TRUSTED))) - return false; return true; } diff --git a/security/manager/ssl/nsNSSCertTrust.h b/security/manager/ssl/nsNSSCertTrust.h index 26617dcb1d93..8f546376346b 100644 --- a/security/manager/ssl/nsNSSCertTrust.h +++ b/security/manager/ssl/nsNSSCertTrust.h @@ -15,22 +15,16 @@ class nsNSSCertTrust { public: nsNSSCertTrust(); - nsNSSCertTrust(unsigned int ssl, unsigned int email, unsigned int objsign); + nsNSSCertTrust(unsigned int ssl, unsigned int email); explicit nsNSSCertTrust(CERTCertTrust *t); virtual ~nsNSSCertTrust(); /* query */ bool HasAnyCA(); bool HasAnyUser(); - bool HasPeer(bool checkSSL = true, - bool checkEmail = true, - bool checkObjSign = true); - bool HasTrustedCA(bool checkSSL = true, - bool checkEmail = true, - bool checkObjSign = true); - bool HasTrustedPeer(bool checkSSL = true, - bool checkEmail = true, - bool checkObjSign = true); + bool HasPeer(bool checkSSL = true, bool checkEmail = true); + bool HasTrustedCA(bool checkSSL = true, bool checkEmail = true); + bool HasTrustedPeer(bool checkSSL = true, bool checkEmail = true); /* common defaults */ /* equivalent to "c,c,c" */ @@ -48,14 +42,10 @@ public: bool ca, bool tCA, bool tClientCA, bool user, bool warn); - void SetObjSignTrust(bool peer, bool tPeer, - bool ca, bool tCA, bool tClientCA, - bool user, bool warn); - /* set c <--> CT */ - void AddCATrust(bool ssl, bool email, bool objSign); + void AddCATrust(bool ssl, bool email); /* set p <--> P */ - void AddPeerTrust(bool ssl, bool email, bool objSign); + void AddPeerTrust(bool ssl, bool email); CERTCertTrust& GetTrust() { return mTrust; } diff --git a/security/manager/ssl/nsNSSCertificateDB.cpp b/security/manager/ssl/nsNSSCertificateDB.cpp index d5cda5499861..46e77e660add 100644 --- a/security/manager/ssl/nsNSSCertificateDB.cpp +++ b/security/manager/ssl/nsNSSCertificateDB.cpp @@ -382,8 +382,7 @@ nsNSSCertificateDB::handleCACertDownload(NotNull x509Certs, nsNSSCertTrust trust; trust.SetValidCA(); trust.AddCATrust(!!(trustBits & nsIX509CertDB::TRUSTED_SSL), - !!(trustBits & nsIX509CertDB::TRUSTED_EMAIL), - !!(trustBits & nsIX509CertDB::TRUSTED_OBJSIGN)); + !!(trustBits & nsIX509CertDB::TRUSTED_EMAIL)); UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE, @@ -737,7 +736,7 @@ nsNSSCertificateDB::DeleteCertificate(nsIX509Cert *aCert) // want to do that with user certs, because a user may re-store // the cert onto the card again at which point we *will* want to // trust that cert if it chains up properly. - nsNSSCertTrust trust(0, 0, 0); + nsNSSCertTrust trust(0, 0); srv = ChangeCertTrustWithPossibleAuthentication(cert, trust.GetTrust(), nullptr); } @@ -768,17 +767,15 @@ nsNSSCertificateDB::SetCertTrust(nsIX509Cert *cert, case nsIX509Cert::CA_CERT: trust.SetValidCA(); trust.AddCATrust(!!(trusted & nsIX509CertDB::TRUSTED_SSL), - !!(trusted & nsIX509CertDB::TRUSTED_EMAIL), - !!(trusted & nsIX509CertDB::TRUSTED_OBJSIGN)); + !!(trusted & nsIX509CertDB::TRUSTED_EMAIL)); break; case nsIX509Cert::SERVER_CERT: trust.SetValidPeer(); - trust.AddPeerTrust(trusted & nsIX509CertDB::TRUSTED_SSL, false, false); + trust.AddPeerTrust(trusted & nsIX509CertDB::TRUSTED_SSL, false); break; case nsIX509Cert::EMAIL_CERT: trust.SetValidPeer(); - trust.AddPeerTrust(false, !!(trusted & nsIX509CertDB::TRUSTED_EMAIL), - false); + trust.AddPeerTrust(false, !!(trusted & nsIX509CertDB::TRUSTED_EMAIL)); break; default: // Ignore any other type of certificate (including invalid types). @@ -821,31 +818,25 @@ nsNSSCertificateDB::IsCertTrusted(nsIX509Cert *cert, nsNSSCertTrust trust(&nsstrust); if (certType == nsIX509Cert::CA_CERT) { if (trustType & nsIX509CertDB::TRUSTED_SSL) { - *_isTrusted = trust.HasTrustedCA(true, false, false); + *_isTrusted = trust.HasTrustedCA(true, false); } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) { - *_isTrusted = trust.HasTrustedCA(false, true, false); - } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) { - *_isTrusted = trust.HasTrustedCA(false, false, true); + *_isTrusted = trust.HasTrustedCA(false, true); } else { return NS_ERROR_FAILURE; } } else if (certType == nsIX509Cert::SERVER_CERT) { if (trustType & nsIX509CertDB::TRUSTED_SSL) { - *_isTrusted = trust.HasTrustedPeer(true, false, false); + *_isTrusted = trust.HasTrustedPeer(true, false); } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) { - *_isTrusted = trust.HasTrustedPeer(false, true, false); - } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) { - *_isTrusted = trust.HasTrustedPeer(false, false, true); + *_isTrusted = trust.HasTrustedPeer(false, true); } else { return NS_ERROR_FAILURE; } } else if (certType == nsIX509Cert::EMAIL_CERT) { if (trustType & nsIX509CertDB::TRUSTED_SSL) { - *_isTrusted = trust.HasTrustedPeer(true, false, false); + *_isTrusted = trust.HasTrustedPeer(true, false); } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) { - *_isTrusted = trust.HasTrustedPeer(false, true, false); - } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) { - *_isTrusted = trust.HasTrustedPeer(false, false, true); + *_isTrusted = trust.HasTrustedPeer(false, true); } else { return NS_ERROR_FAILURE; } diff --git a/security/manager/ssl/tests/mochitest/browser/browser_downloadCert_ui.js b/security/manager/ssl/tests/mochitest/browser/browser_downloadCert_ui.js index c1e466f9aefc..f63a58331843 100644 --- a/security/manager/ssl/tests/mochitest/browser/browser_downloadCert_ui.js +++ b/security/manager/ssl/tests/mochitest/browser/browser_downloadCert_ui.js @@ -122,7 +122,6 @@ add_task(async function testAcceptDialogReturnValues() { let [win, retVals] = await openCertDownloadDialog(TEST_CASES[0].cert); win.document.getElementById("trustSSL").checked = true; win.document.getElementById("trustEmail").checked = false; - win.document.getElementById("trustObjSign").checked = true; info("Accepting dialog"); win.document.getElementById("download_cert").acceptDialog(); await BrowserTestUtils.windowClosed(win); @@ -133,8 +132,6 @@ add_task(async function testAcceptDialogReturnValues() { "Return value should signal SSL trust checkbox was checked"); Assert.ok(!retVals.get("trustForEmail"), "Return value should signal E-mail trust checkbox was unchecked"); - Assert.ok(retVals.get("trustForObjSign"), - "Return value should signal Obj Sign trust checkbox was checked"); }); // Test that the right values are returned when the dialog is canceled. diff --git a/security/manager/ssl/tests/mochitest/browser/browser_editCACertTrust.js b/security/manager/ssl/tests/mochitest/browser/browser_editCACertTrust.js index bebc9ba2b305..cb2490c14273 100644 --- a/security/manager/ssl/tests/mochitest/browser/browser_editCACertTrust.js +++ b/security/manager/ssl/tests/mochitest/browser/browser_editCACertTrust.js @@ -33,7 +33,7 @@ function openEditCertTrustDialog() { } add_task(async function setup() { - // Initially trust ca.pem for SSL, but not e-mail or object signing. + // Initially trust ca.pem for SSL but not e-mail. gCert = await readCertificate("ca.pem", "CT,,"); Assert.ok(gCertDB.isCertTrusted(gCert, Ci.nsIX509Cert.CA_CERT, Ci.nsIX509CertDB.TRUSTED_SSL), @@ -41,9 +41,6 @@ add_task(async function setup() { Assert.ok(!gCertDB.isCertTrusted(gCert, Ci.nsIX509Cert.CA_CERT, Ci.nsIX509CertDB.TRUSTED_EMAIL), "Sanity check: ca.pem should not be trusted for e-mail"); - Assert.ok(!gCertDB.isCertTrusted(gCert, Ci.nsIX509Cert.CA_CERT, - Ci.nsIX509CertDB.TRUSTED_OBJSIGN), - "Sanity check: ca.pem should not be trusted for object signing"); }); // Tests the following: @@ -55,13 +52,10 @@ add_task(async function testAcceptDialog() { let sslCheckbox = win.document.getElementById("trustSSL"); let emailCheckbox = win.document.getElementById("trustEmail"); - let objSignCheckbox = win.document.getElementById("trustObjSign"); Assert.ok(sslCheckbox.checked, "Cert should be trusted for SSL in UI"); Assert.ok(!emailCheckbox.checked, "Cert should not be trusted for e-mail in UI"); - Assert.ok(!objSignCheckbox.checked, - "Cert should not be trusted for object signing in UI"); sslCheckbox.checked = false; emailCheckbox.checked = true; @@ -76,9 +70,6 @@ add_task(async function testAcceptDialog() { Assert.ok(gCertDB.isCertTrusted(gCert, Ci.nsIX509Cert.CA_CERT, Ci.nsIX509CertDB.TRUSTED_EMAIL), "Cert should now be trusted for e-mail"); - Assert.ok(!gCertDB.isCertTrusted(gCert, Ci.nsIX509Cert.CA_CERT, - Ci.nsIX509CertDB.TRUSTED_OBJSIGN), - "Cert should still not be trusted for object signing"); }); // Tests the following: @@ -90,17 +81,13 @@ add_task(async function testCancelDialog() { let sslCheckbox = win.document.getElementById("trustSSL"); let emailCheckbox = win.document.getElementById("trustEmail"); - let objSignCheckbox = win.document.getElementById("trustObjSign"); Assert.ok(!sslCheckbox.checked, "Cert should not be trusted for SSL in UI"); Assert.ok(emailCheckbox.checked, "Cert should be trusted for e-mail in UI"); - Assert.ok(!objSignCheckbox.checked, - "Cert should not be trusted for object signing in UI"); sslCheckbox.checked = true; emailCheckbox.checked = false; - objSignCheckbox.checked = true; info("Canceling dialog"); win.document.getElementById("editCaCert").cancelDialog(); @@ -112,7 +99,4 @@ add_task(async function testCancelDialog() { Assert.ok(gCertDB.isCertTrusted(gCert, Ci.nsIX509Cert.CA_CERT, Ci.nsIX509CertDB.TRUSTED_EMAIL), "Cert should still be trusted for e-mail"); - Assert.ok(!gCertDB.isCertTrusted(gCert, Ci.nsIX509Cert.CA_CERT, - Ci.nsIX509CertDB.TRUSTED_OBJSIGN), - "Cert should still not be trusted for object signing"); }); diff --git a/security/manager/ssl/tests/unit/test_cert_trust.js b/security/manager/ssl/tests/unit/test_cert_trust.js index 560b1a3f702c..8bd3e32fb70b 100644 --- a/security/manager/ssl/tests/unit/test_cert_trust.js +++ b/security/manager/ssl/tests/unit/test_cert_trust.js @@ -18,8 +18,7 @@ function load_cert(cert_name, trust_string) { function setup_basic_trusts(ca_cert, int_cert) { certdb.setCertTrust(ca_cert, Ci.nsIX509Cert.CA_CERT, Ci.nsIX509CertDB.TRUSTED_SSL | - Ci.nsIX509CertDB.TRUSTED_EMAIL | - Ci.nsIX509CertDB.TRUSTED_OBJSIGN); + Ci.nsIX509CertDB.TRUSTED_EMAIL); certdb.setCertTrust(int_cert, Ci.nsIX509Cert.CA_CERT, 0); } diff --git a/servo/components/script/dom/bindings/str.rs b/servo/components/script/dom/bindings/str.rs index 3667dddf92d4..596b2db14b2e 100644 --- a/servo/components/script/dom/bindings/str.rs +++ b/servo/components/script/dom/bindings/str.rs @@ -208,6 +208,91 @@ impl DOMString { self.0.truncate(last_non_whitespace); let _ = self.0.splice(0..first_non_whitespace, ""); } + + /// Validates this `DOMString` is a time string according to + /// . + pub fn is_valid_time_string(&self) -> bool { + enum State { + HourHigh, + HourLow09, + HourLow03, + MinuteColon, + MinuteHigh, + MinuteLow, + SecondColon, + SecondHigh, + SecondLow, + MilliStop, + MilliHigh, + MilliMiddle, + MilliLow, + Done, + Error, + } + let next_state = |valid: bool, next: State| -> State { if valid { next } else { State::Error } }; + + let state = self.chars().fold(State::HourHigh, |state, c| { + match state { + // Step 1 "HH" + State::HourHigh => { + match c { + '0' | '1' => State::HourLow09, + '2' => State::HourLow03, + _ => State::Error, + } + }, + State::HourLow09 => next_state(c.is_digit(10), State::MinuteColon), + State::HourLow03 => next_state(c.is_digit(4), State::MinuteColon), + + // Step 2 ":" + State::MinuteColon => next_state(c == ':', State::MinuteHigh), + + // Step 3 "mm" + State::MinuteHigh => next_state(c.is_digit(6), State::MinuteLow), + State::MinuteLow => next_state(c.is_digit(10), State::SecondColon), + + // Step 4.1 ":" + State::SecondColon => next_state(c == ':', State::SecondHigh), + // Step 4.2 "ss" + State::SecondHigh => next_state(c.is_digit(6), State::SecondLow), + State::SecondLow => next_state(c.is_digit(10), State::MilliStop), + + // Step 4.3.1 "." + State::MilliStop => next_state(c == '.', State::MilliHigh), + // Step 4.3.2 "SSS" + State::MilliHigh => next_state(c.is_digit(6), State::MilliMiddle), + State::MilliMiddle => next_state(c.is_digit(10), State::MilliLow), + State::MilliLow => next_state(c.is_digit(10), State::Done), + + _ => State::Error, + } + }); + + match state { + State::Done | + // Step 4 (optional) + State::SecondColon | + // Step 4.3 (optional) + State::MilliStop | + // Step 4.3.2 (only 1 digit required) + State::MilliMiddle | State::MilliLow => true, + _ => false + } + } + + /// A valid date string should be "YYYY-MM-DD" + /// YYYY must be four or more digits, MM and DD both must be two digits + /// https://html.spec.whatwg.org/multipage/#valid-date-string + pub fn is_valid_date_string(&self) -> bool { + parse_date_string(&*self.0).is_ok() + } + + /// A valid month string should be "YYYY-MM" + /// YYYY must be four or more digits, MM both must be two digits + /// https://html.spec.whatwg.org/multipage/#valid-month-string + pub fn is_valid_month_string(&self) -> bool { + parse_month_string(&*self.0).is_ok() + } } impl Borrow for DOMString { @@ -332,3 +417,90 @@ impl Extend for DOMString { self.0.extend(iterable) } } + +/// https://html.spec.whatwg.org/multipage/#parse-a-month-string +fn parse_month_string(value: &str) -> Result<(u32, u32), ()> { + // Step 1, 2, 3 + let (year_int, month_int) = parse_month_component(value)?; + + // Step 4 + if value.split("-").nth(2).is_some() { + return Err(()); + } + // Step 5 + Ok((year_int, month_int)) +} + +/// https://html.spec.whatwg.org/multipage/#parse-a-date-string +fn parse_date_string(value: &str) -> Result<(u32, u32, u32), ()> { + // Step 1, 2, 3 + let (year_int, month_int, day_int) = parse_date_component(value)?; + + // Step 4 + if value.split('-').nth(3).is_some() { + return Err(()); + } + + // Step 5, 6 + Ok((year_int, month_int, day_int)) +} + +/// https://html.spec.whatwg.org/multipage/#parse-a-month-component +fn parse_month_component(value: &str) -> Result<(u32, u32), ()> { + // Step 3 + let mut iterator = value.split('-'); + let year = iterator.next().ok_or(())?; + let month = iterator.next().ok_or(())?; + + // Step 1, 2 + let year_int = year.parse::().map_err(|_| ())?; + if year.len() < 4 || year_int == 0 { + return Err(()); + } + + // Step 4, 5 + let month_int = month.parse::().map_err(|_| ())?; + if month.len() != 2 || month_int > 12 || month_int < 1 { + return Err(()); + } + + // Step 6 + Ok((year_int, month_int)) +} + +/// https://html.spec.whatwg.org/multipage/#parse-a-date-component +fn parse_date_component(value: &str) -> Result<(u32, u32, u32), ()> { + // Step 1 + let (year_int, month_int) = parse_month_component(value)?; + + // Step 3, 4 + let day = value.split('-').nth(2).ok_or(())?; + let day_int = day.parse::().map_err(|_| ())?; + if day.len() != 2 { + return Err(()); + } + + // Step 2, 5 + let max_day = max_day_in_month(year_int, month_int)?; + if day_int == 0 || day_int > max_day { + return Err(()); + } + + // Step 6 + Ok((year_int, month_int, day_int)) +} + +fn max_day_in_month(year_num: u32, month_num: u32) -> Result { + match month_num { + 1|3|5|7|8|10|12 => Ok(31), + 4|6|9|11 => Ok(30), + 2 => { + if year_num % 400 == 0 || (year_num % 4 == 0 && year_num % 100 != 0) { + Ok(29) + } else { + Ok(28) + } + }, + _ => Err(()) + } +} diff --git a/servo/components/script/dom/htmlinputelement.rs b/servo/components/script/dom/htmlinputelement.rs index 7f995671d8dd..cbf4f483e0c8 100755 --- a/servo/components/script/dom/htmlinputelement.rs +++ b/servo/components/script/dom/htmlinputelement.rs @@ -875,6 +875,18 @@ impl HTMLInputElement { content.strip_newlines(); content.strip_leading_and_trailing_ascii_whitespace(); } + atom!("date") => { + let mut textinput = self.textinput.borrow_mut(); + if !textinput.single_line_content().is_valid_date_string() { + *textinput.single_line_content_mut() = "".into(); + } + } + atom!("month") => { + let mut textinput = self.textinput.borrow_mut(); + if !textinput.single_line_content().is_valid_month_string() { + *textinput.single_line_content_mut() = "".into(); + } + } atom!("color") => { let mut textinput = self.textinput.borrow_mut(); @@ -895,6 +907,13 @@ impl HTMLInputElement { textinput.set_content("#000000".into()); } } + atom!("time") => { + let mut textinput = self.textinput.borrow_mut(); + + if ! textinput.single_line_content().is_valid_time_string() { + *textinput.single_line_content_mut() = "".into(); + } + } // TODO: Implement more value sanitization algorithms for different types of inputs _ => () } @@ -1035,6 +1054,7 @@ impl VirtualMethods for HTMLInputElement { let value = mutation.new_value(attr).map(|value| (**value).to_owned()); self.textinput.borrow_mut().set_content( value.map_or(DOMString::new(), DOMString::from)); + self.sanitize_value(); self.update_placeholder_shown_state(); }, &local_name!("name") if self.input_type.get() == InputType::InputRadio => { @@ -1110,7 +1130,6 @@ impl VirtualMethods for HTMLInputElement { if let Some(ref s) = self.super_type() { s.bind_to_tree(tree_in_doc); } - self.upcast::().check_ancestors_disabled_state_for_form_control(); } diff --git a/servo/etc/ci/performance/test_all.sh b/servo/etc/ci/performance/test_all.sh index d62ddd5abeca..738fbc2204f6 100755 --- a/servo/etc/ci/performance/test_all.sh +++ b/servo/etc/ci/performance/test_all.sh @@ -43,6 +43,9 @@ fi echo "Starting the local server" python3 -m http.server > /dev/null 2>&1 & +# Stop the local server no matter how we exit the script +trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT + # TODO: enable the full manifest when #11087 is fixed # https://github.com/servo/servo/issues/11087 # MANIFEST="page_load_test/tp5n/20160509.manifest" @@ -61,5 +64,3 @@ then python3 submit_to_s3.py "${PERF_FILE}" "${PERF_KEY}" fi -echo "Stopping the local server" -trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT diff --git a/storage/mozIStorageAsyncConnection.idl b/storage/mozIStorageAsyncConnection.idl index 933ed3426dad..c3663be6e077 100644 --- a/storage/mozIStorageAsyncConnection.idl +++ b/storage/mozIStorageAsyncConnection.idl @@ -95,6 +95,11 @@ interface mozIStorageAsyncConnection : nsISupports { * - journal_size_limit * - synchronous * - wal_autocheckpoint + * All SQL functions are copied over to read-only and writeable clones. + * Additionally, all temporary tables, triggers, and views, as well as + * any indexes on temporary tables, are copied over to writeable clones. + * For temporary tables, only the schemas are copied, not their + * contents. */ void asyncClone(in boolean aReadOnly, in mozIStorageCompletionCallback aCallback); diff --git a/storage/mozIStorageConnection.idl b/storage/mozIStorageConnection.idl index 11d8aa5acdd1..bdc7305216c0 100644 --- a/storage/mozIStorageConnection.idl +++ b/storage/mozIStorageConnection.idl @@ -82,6 +82,11 @@ interface mozIStorageConnection : mozIStorageAsyncConnection { * - journal_size_limit * - synchronous * - wal_autocheckpoint + * All SQL functions are copied over to read-only and writeable clones. + * Additionally, all temporary tables, triggers, and views, as well as + * any indexes on temporary tables, are copied over to writeable clones. + * For temporary tables, only the schemas are copied, not their + * contents. * */ mozIStorageConnection clone([optional] in boolean aReadOnly); diff --git a/storage/mozStorageConnection.cpp b/storage/mozStorageConnection.cpp index 69785dc5b583..dd415c652ebd 100644 --- a/storage/mozStorageConnection.cpp +++ b/storage/mozStorageConnection.cpp @@ -1526,6 +1526,39 @@ Connection::initializeClone(Connection* aClone, bool aReadOnly) return rv; } + // Copy over temporary tables, triggers, and views from the original + // connections. Entities in `sqlite_temp_master` are only visible to the + // connection that created them. + if (!aReadOnly) { + nsCOMPtr stmt; + rv = CreateStatement(NS_LITERAL_CSTRING("SELECT sql FROM sqlite_temp_master " + "WHERE type IN ('table', 'view', " + "'index', 'trigger')"), + getter_AddRefs(stmt)); + // Propagate errors, because failing to copy triggers might cause schema + // coherency issues when writing to the database from the cloned connection. + NS_ENSURE_SUCCESS(rv, rv); + bool hasResult = false; + while (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { + nsAutoCString query; + rv = stmt->GetUTF8String(0, query); + NS_ENSURE_SUCCESS(rv, rv); + + // The `CREATE` SQL statements in `sqlite_temp_master` omit the `TEMP` + // keyword. We need to add it back, or we'll recreate temporary entities + // as persistent ones. `sqlite_temp_master` also holds `CREATE INDEX` + // statements, but those don't need `TEMP` keywords. + if (StringBeginsWith(query, NS_LITERAL_CSTRING("CREATE TABLE ")) || + StringBeginsWith(query, NS_LITERAL_CSTRING("CREATE TRIGGER ")) || + StringBeginsWith(query, NS_LITERAL_CSTRING("CREATE VIEW "))) { + query.Replace(0, 6, "CREATE TEMP"); + } + + rv = aClone->ExecuteSimpleSQL(query); + NS_ENSURE_SUCCESS(rv, rv); + } + } + // Re-attach on-disk databases that were attached to the original connection. { nsCOMPtr stmt; diff --git a/storage/test/unit/test_storage_connection.js b/storage/test/unit/test_storage_connection.js index bf831828f0bf..0938c4a72d94 100644 --- a/storage/test/unit/test_storage_connection.js +++ b/storage/test/unit/test_storage_connection.js @@ -753,6 +753,148 @@ add_task(async function test_clone_attach_database() { db3.close(); }); +add_task(async function test_async_clone_with_temp_trigger_and_table() { + do_print("Open connection"); + let db = getService().openDatabase(getTestDB()); + do_check_true(db instanceof Ci.mozIStorageAsyncConnection); + + do_print("Set up tables on original connection"); + let createQueries = [ + `CREATE TEMP TABLE test_temp(name TEXT)`, + `CREATE INDEX test_temp_idx ON test_temp(name)`, + `CREATE TEMP TRIGGER test_temp_afterdelete_trigger + AFTER DELETE ON test_temp FOR EACH ROW + BEGIN + INSERT INTO test(name) VALUES(OLD.name); + END`]; + for (let query of createQueries) { + let stmt = db.createAsyncStatement(query); + await executeAsync(stmt); + stmt.finalize(); + } + + do_print("Create read-write clone with temp tables"); + let readWriteClone = await asyncClone(db, false); + do_check_true(readWriteClone instanceof Ci.mozIStorageAsyncConnection); + + do_print("Insert into temp table on read-write clone"); + let insertStmt = readWriteClone.createAsyncStatement(` + INSERT INTO test_temp(name) VALUES('mak'), ('standard8'), ('markh')`); + await executeAsync(insertStmt); + insertStmt.finalize(); + + do_print("Fire temp trigger on read-write clone"); + let deleteStmt = readWriteClone.createAsyncStatement(` + DELETE FROM test_temp`); + await executeAsync(deleteStmt); + deleteStmt.finalize(); + + do_print("Read from original connection"); + let names = []; + let readStmt = db.createStatement(`SELECT name FROM test`); + while (readStmt.executeStep()) { + names.push(readStmt.getUTF8String(0)); + } + readStmt.finalize(); + do_check_true(names.includes("mak")); + do_check_true(names.includes("standard8")); + do_check_true(names.includes("markh")); + + do_print("Create read-only clone"); + let readOnlyClone = await asyncClone(db, true); + do_check_true(readOnlyClone instanceof Ci.mozIStorageAsyncConnection); + + do_print("Read-only clone shouldn't have temp entities"); + let badStmt = readOnlyClone.createAsyncStatement(`SELECT 1 FROM test_temp`); + await Assert.rejects(executeAsync(badStmt)); + badStmt.finalize(); + + do_print("Clean up"); + for (let conn of [db, readWriteClone, readOnlyClone]) { + await asyncClose(conn); + } +}); + +add_task(async function test_sync_clone_in_transaction() { + do_print("Open connection"); + let db = getService().openDatabase(getTestDB()); + do_check_true(db instanceof Ci.mozIStorageAsyncConnection); + + do_print("Begin transaction on main connection"); + db.beginTransaction(); + + do_print("Create temp table and trigger in transaction"); + let createQueries = [ + `CREATE TEMP TABLE test_temp(name TEXT)`, + `CREATE TEMP TRIGGER test_temp_afterdelete_trigger + AFTER DELETE ON test_temp FOR EACH ROW + BEGIN + INSERT INTO test(name) VALUES(OLD.name); + END`, + ]; + for (let query of createQueries) { + db.executeSimpleSQL(query); + } + + do_print("Clone main connection while transaction is in progress"); + let clone = db.clone(/* aReadOnly */ false); + + // Dropping the table also drops `test_temp_afterdelete_trigger`. + do_print("Drop temp table on main connection"); + db.executeSimpleSQL(`DROP TABLE test_temp`); + + do_print("Commit transaction"); + db.commitTransaction(); + + do_print("Clone connection should still have temp entities"); + let readTempStmt = clone.createStatement(`SELECT 1 FROM test_temp`); + readTempStmt.execute(); + readTempStmt.finalize(); + + do_print("Clean up"); + + db.close(); + clone.close(); +}); + +add_task(async function test_sync_clone_with_function() { + do_print("Open connection"); + let db = getService().openDatabase(getTestDB()); + do_check_true(db instanceof Ci.mozIStorageAsyncConnection); + + do_print("Create SQL function"); + function storeLastInsertedNameFunc() { + this.name = null; + } + storeLastInsertedNameFunc.prototype = { + onFunctionCall(args) { + this.name = args.getUTF8String(0); + }, + }; + let func = new storeLastInsertedNameFunc(); + db.createFunction("store_last_inserted_name", 1, func); + + do_print("Create temp trigger on main connection"); + db.executeSimpleSQL(` + CREATE TEMP TRIGGER test_afterinsert_trigger + AFTER INSERT ON test FOR EACH ROW + BEGIN + SELECT store_last_inserted_name(NEW.name); + END`); + + do_print("Clone main connection"); + let clone = db.clone(/* aReadOnly */ false); + + do_print("Write to clone"); + clone.executeSimpleSQL(`INSERT INTO test(name) VALUES('kit')`); + + do_check_eq(func.name, "kit"); + + do_print("Clean up"); + db.close(); + clone.close(); +}); + add_task(async function test_getInterface() { let db = getOpenedDatabase(); let target = db.QueryInterface(Ci.nsIInterfaceRequestor) diff --git a/taskcluster/docs/taskgraph.rst b/taskcluster/docs/taskgraph.rst index e211a5dc4c52..f219ceba4c7e 100644 --- a/taskcluster/docs/taskgraph.rst +++ b/taskcluster/docs/taskgraph.rst @@ -145,9 +145,16 @@ So for instance, if you had already requested a build task in the ``try`` comman and you wish to add a test which depends on this build, the original build task is re-used. -Action Tasks are currently scheduled by -[pulse_actions](https://github.com/mozilla/pulse_actions). This feature is only -present on ``try`` pushes for now. + +Runnable jobs +------------- +As part of the execution of the Gecko decision task we generate a +``public/runnable-jobs.json.gz`` file. It contains a subset of all the data +contained within the ``full-task-graph.json``. + +This file has the minimum ammount of data needed by Treeherder to show all +tasks that can be scheduled on a push. + Task Parameterization --------------------- diff --git a/taskcluster/taskgraph/decision.py b/taskcluster/taskgraph/decision.py index b7fd1fef0afd..de1396689fab 100644 --- a/taskcluster/taskgraph/decision.py +++ b/taskcluster/taskgraph/decision.py @@ -89,6 +89,25 @@ PER_PROJECT_PARAMETERS = { } +def full_task_graph_to_runnable_jobs(full_task_json): + runnable_jobs = {} + for label, node in full_task_json.iteritems(): + if not ('extra' in node['task'] and 'treeherder' in node['task']['extra']): + continue + + th = node['task']['extra']['treeherder'] + runnable_jobs[label] = { + 'symbol': th['symbol'] + } + + for i in ('groupName', 'groupSymbol', 'collection'): + if i in th: + runnable_jobs[label][i] = th[i] + if th.get('machine', {}).get('platform'): + runnable_jobs[label]['platform'] = th['machine']['platform'] + return runnable_jobs + + def taskgraph_decision(options, parameters=None): """ Run the decision task. This function implements `mach taskgraph decision`, @@ -118,6 +137,9 @@ def taskgraph_decision(options, parameters=None): full_task_json = tgg.full_task_graph.to_json() write_artifact('full-task-graph.json', full_task_json) + # write out the public/runnable-jobs.json.gz file + write_artifact('runnable-jobs.json.gz', full_task_graph_to_runnable_jobs(full_task_json)) + # this is just a test to check whether the from_json() function is working _, _ = TaskGraph.from_json(full_task_json) @@ -259,5 +281,9 @@ def write_artifact(filename, data): elif filename.endswith('.json'): with open(path, 'w') as f: json.dump(data, f, sort_keys=True, indent=2, separators=(',', ': ')) + elif filename.endswith('.gz'): + import gzip + with gzip.open(path, 'wb') as f: + f.write(json.dumps(data)) else: raise TypeError("Don't know how to write to {}".format(filename)) diff --git a/taskcluster/taskgraph/test/python.ini b/taskcluster/taskgraph/test/python.ini index 2e046977e664..5fac582e51cd 100644 --- a/taskcluster/taskgraph/test/python.ini +++ b/taskcluster/taskgraph/test/python.ini @@ -18,6 +18,7 @@ subsuite = taskgraph [test_util_docker.py] [test_util_parameterization.py] [test_util_python_path.py] +[test_util_runnable_jobs.py] [test_util_schema.py] [test_util_templates.py] [test_util_time.py] diff --git a/taskcluster/taskgraph/test/test_util_runnable_jobs.py b/taskcluster/taskgraph/test/test_util_runnable_jobs.py new file mode 100644 index 000000000000..3559ad396f4d --- /dev/null +++ b/taskcluster/taskgraph/test/test_util_runnable_jobs.py @@ -0,0 +1,83 @@ +# 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/. + +from __future__ import absolute_import + +import unittest + +from taskgraph.decision import full_task_graph_to_runnable_jobs +from taskgraph.graph import Graph +from taskgraph.taskgraph import TaskGraph +from taskgraph.task import Task +from mozunit import main + + +class TestRunnableJobs(unittest.TestCase): + + tasks = [ + { + 'kind': 'build', + 'label': 'a', + 'attributes': {}, + 'task': { + 'extra': { + 'treeherder': { + 'symbol': 'B' + } + }, + } + }, + { + 'kind': 'test', + 'label': 'b', + 'attributes': {}, + 'task': { + 'extra': { + 'treeherder': { + 'collection': { + 'opt': True + }, + 'groupName': 'Some group', + 'groupSymbol': 'GS', + 'machine': { + 'platform': 'linux64' + }, + 'symbol': 't' + } + }, + } + }, + ] + + def make_taskgraph(self, tasks): + label_to_taskid = {k: k + '-tid' for k in tasks} + for label, task_id in label_to_taskid.iteritems(): + tasks[label].task_id = task_id + graph = Graph(nodes=set(tasks), edges=set()) + taskgraph = TaskGraph(tasks, graph) + return taskgraph, label_to_taskid + + def test_taskgraph_to_runnable_jobs(self): + tg, label_to_taskid = self.make_taskgraph({ + t['label']: Task(**t) for t in self.tasks[:] + }) + + res = full_task_graph_to_runnable_jobs(tg.to_json()) + + self.assertEqual(res, { + 'a': { + 'symbol': 'B' + }, + 'b': { + 'collection': {'opt': True}, + 'groupName': 'Some group', + 'groupSymbol': 'GS', + 'symbol': 't', + 'platform': 'linux64' + } + }) + + +if __name__ == '__main__': + main() diff --git a/toolkit/components/payments/jar.mn b/toolkit/components/payments/jar.mn index d577756f2caa..044272304894 100644 --- a/toolkit/components/payments/jar.mn +++ b/toolkit/components/payments/jar.mn @@ -11,6 +11,7 @@ toolkit.jar: % resource payments %res/payments/ res/payments (res/paymentRequest.*) res/payments/components/ (res/components/*.js) + res/payments/containers/ (res/containers/*.js) res/payments/debugging.html (res/debugging.html) res/payments/debugging.js (res/debugging.js) res/payments/mixins/ (res/mixins/*.js) diff --git a/toolkit/components/payments/res/components/currency-amount.js b/toolkit/components/payments/res/components/currency-amount.js index 1cc0bd62f84b..a7024f2e006f 100644 --- a/toolkit/components/payments/res/components/currency-amount.js +++ b/toolkit/components/payments/res/components/currency-amount.js @@ -2,6 +2,8 @@ * 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"; + /** * */ diff --git a/toolkit/components/payments/res/containers/payment-dialog.js b/toolkit/components/payments/res/containers/payment-dialog.js new file mode 100644 index 000000000000..dcafbca84225 --- /dev/null +++ b/toolkit/components/payments/res/containers/payment-dialog.js @@ -0,0 +1,55 @@ +/* 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/. */ + +/* global PaymentStateSubscriberMixin, PaymentRequest */ + +"use strict"; + +/** + * + */ + +class PaymentDialog extends PaymentStateSubscriberMixin(HTMLElement) { + constructor() { + super(); + this._template = document.getElementById("payment-dialog-template"); + } + + connectedCallback() { + let contents = document.importNode(this._template.content, true); + this._hostNameEl = contents.querySelector("#host-name"); + + this._cancelButton = contents.querySelector("#cancel"); + this._cancelButton.addEventListener("click", this.cancelRequest); + + this.appendChild(contents); + + super.connectedCallback(); + } + + disconnectedCallback() { + this._cancelButtonEl.removeEventListener("click", this.cancelRequest); + super.disconnectedCallback(); + } + + cancelRequest() { + PaymentRequest.cancel(); + } + + setLoadingState(state) { + this.requestStore.setState(state); + } + + render(state) { + let request = state.request; + this._hostNameEl.textContent = request.topLevelPrincipal.URI.displayHost; + + let totalItem = request.paymentDetails.totalItem; + let totalAmountEl = this.querySelector("#total > currency-amount"); + totalAmountEl.value = totalItem.amount.value; + totalAmountEl.currency = totalItem.amount.currency; + } +} + +customElements.define("payment-dialog", PaymentDialog); diff --git a/toolkit/components/payments/res/debugging.html b/toolkit/components/payments/res/debugging.html index 0c044220a5f8..f42976ca7b9b 100644 --- a/toolkit/components/payments/res/debugging.html +++ b/toolkit/components/payments/res/debugging.html @@ -11,6 +11,9 @@
                    + + +
                    diff --git a/toolkit/components/payments/res/debugging.js b/toolkit/components/payments/res/debugging.js index 6d5ace18a310..19979b783e6c 100644 --- a/toolkit/components/payments/res/debugging.js +++ b/toolkit/components/payments/res/debugging.js @@ -2,10 +2,133 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +const requestStore = window.parent.document.querySelector("payment-dialog").requestStore; + +let REQUEST_1 = { + tabId: 9, + topLevelPrincipal: {URI: {displayHost: "tschaeff.github.io"}}, + requestId: "3797081f-a96b-c34b-a58b-1083c6e66e25", + paymentMethods: [], + paymentDetails: { + id: "", + totalItem: {label: "Demo total", amount: {currency: "EUR", value: "1.00"}, pending: false}, + displayItems: [ + { + label: "Square", + amount: { + currency: "USD", + value: "5", + }, + }, + ], + shippingOptions: [ + { + id: "123", + label: "Fast", + amount: { + currency: "USD", + value: 10, + }, + selected: false, + }, + { + id: "456", + label: "Faster (default)", + amount: { + currency: "USD", + value: 20, + }, + selected: true, + }, + ], + modifiers: null, + error: "", + }, + paymentOptions: { + requestPayerName: false, + requestPayerEmail: false, + requestPayerPhone: false, + requestShipping: false, + shippingType: "shipping", + }, +}; + +let REQUEST_2 = { + tabId: 9, + topLevelPrincipal: {URI: {displayHost: "example.com"}}, + requestId: "3797081f-a96b-c34b-a58b-1083c6e66e25", + paymentMethods: [], + paymentDetails: { + id: "", + totalItem: {label: "Demo total", amount: {currency: "CAD", value: "25.75"}, pending: false}, + displayItems: [ + { + label: "Triangle", + amount: { + currency: "CAD", + value: "3", + }, + }, + { + label: "Circle", + amount: { + currency: "EUR", + value: "10.50", + }, + }, + ], + shippingOptions: [ + { + id: "123", + label: "Fast (default)", + amount: { + currency: "USD", + value: 10, + }, + selected: true, + }, + { + id: "947", + label: "Slow", + amount: { + currency: "USD", + value: 10, + }, + selected: false, + }, + ], + modifiers: null, + error: "", + }, + paymentOptions: { + requestPayerName: false, + requestPayerEmail: false, + requestPayerPhone: false, + requestShipping: false, + shippingType: "shipping", + }, +}; + + let buttonActions = { + logState() { + let state = requestStore.getState(); + // eslint-disable-next-line no-console + console.log(state); + dump(`${JSON.stringify(state, null, 2)}\n`); + }, + refresh() { window.parent.location.reload(true); }, + + setRequest1() { + requestStore.setState({request: REQUEST_1}); + }, + + setRequest2() { + requestStore.setState({request: REQUEST_2}); + }, }; window.addEventListener("click", function onButtonClick(evt) { diff --git a/toolkit/components/payments/res/mixins/ObservedPropertiesMixin.js b/toolkit/components/payments/res/mixins/ObservedPropertiesMixin.js index a7fb6892127f..e065a24e355b 100644 --- a/toolkit/components/payments/res/mixins/ObservedPropertiesMixin.js +++ b/toolkit/components/payments/res/mixins/ObservedPropertiesMixin.js @@ -57,6 +57,9 @@ function ObservedPropertiesMixin(superClass) { } attributeChangedCallback(attr, oldValue, newValue) { + if (super.attributeChangedCallback) { + super.attributeChangedCallback(attr, oldValue, newValue); + } if (oldValue === newValue) { return; } diff --git a/toolkit/components/payments/res/mixins/PaymentStateSubscriberMixin.js b/toolkit/components/payments/res/mixins/PaymentStateSubscriberMixin.js new file mode 100644 index 000000000000..44ce9e0b1076 --- /dev/null +++ b/toolkit/components/payments/res/mixins/PaymentStateSubscriberMixin.js @@ -0,0 +1,82 @@ +/* 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"; + +/* global PaymentsStore */ + +/** + * A mixin for a custom element to observe store changes to information about a payment request. + */ + +/** + * State of the payment request dialog. + */ +let requestStore = new PaymentsStore({ + request: { + tabId: null, + topLevelPrincipal: {URI: {displayHost: null}}, + requestId: null, + paymentMethods: [], + paymentDetails: { + id: null, + totalItem: {label: null, amount: {currency: null, value: null}}, + displayItems: [], + shippingOptions: [], + modifiers: null, + error: "", + }, + paymentOptions: { + requestPayerName: false, + requestPayerEmail: false, + requestPayerPhone: false, + requestShipping: false, + shippingType: "shipping", + }, + }, + savedAddresses: [], + savedBasicCards: [], +}); + + +/* exported PaymentStateSubscriberMixin */ + +/** + * A mixin to render UI based upon the requestStore and get updated when that store changes. + * + * Attaches `requestStore` to the element to give access to the store. + * @param {class} superClass The class to extend + * @returns {class} + */ +function PaymentStateSubscriberMixin(superClass) { + return class PaymentStateSubscriber extends superClass { + constructor() { + super(); + this.requestStore = requestStore; + } + + connectedCallback() { + this.requestStore.subscribe(this); + this.render(this.requestStore.getState()); + if (super.connectedCallback) { + super.connectedCallback(); + } + } + + disconnectedCallback() { + this.requestStore.unsubscribe(this); + if (super.disconnectedCallback) { + super.disconnectedCallback(); + } + } + + /** + * Called by the store upon state changes. + * @param {object} state The current state + */ + stateChangeCallback(state) { + this.render(state); + } + }; +} diff --git a/toolkit/components/payments/res/paymentRequest.js b/toolkit/components/payments/res/paymentRequest.js index 9979c89f847e..67245748884f 100644 --- a/toolkit/components/payments/res/paymentRequest.js +++ b/toolkit/components/payments/res/paymentRequest.js @@ -11,7 +11,6 @@ "use strict"; let PaymentRequest = { - request: null, domReadyPromise: null, init() { @@ -34,15 +33,6 @@ let PaymentRequest = { this.onPaymentRequestLoad(); break; } - case "click": { - switch (event.target.id) { - case "cancel": { - this.onCancel(); - break; - } - } - break; - } case "keypress": { if (event.code != "KeyD" || !event.altKey || !event.ctrlKey) { break; @@ -80,37 +70,29 @@ let PaymentRequest = { switch (messageType) { case "showPaymentRequest": { - this.request = detail.request; - this.onShowPaymentRequest(); + this.onShowPaymentRequest(detail); break; } } }, onPaymentRequestLoad(requestId) { - let cancelBtn = document.getElementById("cancel"); - cancelBtn.addEventListener("click", this, {once: true}); - window.addEventListener("unload", this, {once: true}); this.sendMessageToChrome("paymentDialogReady"); }, - async onShowPaymentRequest() { + async onShowPaymentRequest(detail) { // Handle getting called before the DOM is ready. await this.domReadyPromise; - let hostNameEl = document.getElementById("host-name"); - hostNameEl.textContent = this.request.topLevelPrincipal.URI.displayHost; - - let totalItem = this.request.paymentDetails.totalItem; - let totalEl = document.getElementById("total"); - let currencyEl = totalEl.querySelector("currency-amount"); - currencyEl.value = totalItem.amount.value; - currencyEl.currency = totalItem.amount.currency; - totalEl.querySelector(".label").textContent = totalItem.label; + document.querySelector("payment-dialog").setLoadingState({ + request: detail.request, + savedAddresses: detail.savedAddresses, + savedBasicCards: detail.savedBasicCards, + }); }, - onCancel() { + cancel() { this.sendMessageToChrome("paymentCancel"); }, diff --git a/toolkit/components/payments/res/paymentRequest.xhtml b/toolkit/components/payments/res/paymentRequest.xhtml index 0453be74a153..ceb861e1222e 100644 --- a/toolkit/components/payments/res/paymentRequest.xhtml +++ b/toolkit/components/payments/res/paymentRequest.xhtml @@ -9,20 +9,33 @@ + + + + + + + + + + -
                    -
                    -

                    - -
                    -
                    - -
                    + diff --git a/toolkit/components/payments/test/mochitest/mochitest.ini b/toolkit/components/payments/test/mochitest/mochitest.ini index d2752c498186..c968e88a6c68 100644 --- a/toolkit/components/payments/test/mochitest/mochitest.ini +++ b/toolkit/components/payments/test/mochitest/mochitest.ini @@ -1,10 +1,14 @@ [DEFAULT] support-files = + ../../../../../testing/modules/sinon-2.3.2.js + ../../res/PaymentsStore.js ../../res/components/currency-amount.js ../../res/mixins/ObservedPropertiesMixin.js + ../../res/mixins/PaymentStateSubscriberMixin.js ../../res/vendor/custom-elements.min.js ../../res/vendor/custom-elements.min.js.map payments_common.js [test_currency_amount.html] [test_ObservedPropertiesMixin.html] +[test_PaymentStateSubscriberMixin.html] diff --git a/toolkit/components/payments/test/mochitest/test_PaymentStateSubscriberMixin.html b/toolkit/components/payments/test/mochitest/test_PaymentStateSubscriberMixin.html new file mode 100644 index 000000000000..208885eba1dd --- /dev/null +++ b/toolkit/components/payments/test/mochitest/test_PaymentStateSubscriberMixin.html @@ -0,0 +1,82 @@ + + + + + + Test the PaymentStateSubscriberMixin + + + + + + + + + + +

                    + +

                    + +
                    +
                    + + + + diff --git a/toolkit/components/places/nsAnnotationService.cpp b/toolkit/components/places/nsAnnotationService.cpp index 0599d51ce896..e481c526f0cc 100644 --- a/toolkit/components/places/nsAnnotationService.cpp +++ b/toolkit/components/places/nsAnnotationService.cpp @@ -47,6 +47,32 @@ const int32_t nsAnnotationService::kAnnoIndex_Type = 6; const int32_t nsAnnotationService::kAnnoIndex_DateAdded = 7; const int32_t nsAnnotationService::kAnnoIndex_LastModified = 8; +namespace { + +// Fires `onItemChanged` notifications for bookmark annotation changes. +void +NotifyItemChanged(const BookmarkData& aBookmark, const nsACString& aName, + uint16_t aSource, bool aDontUpdateLastModified) +{ + if (aBookmark.id < 0) { + return; + } + + ItemChangeData changeData; + changeData.bookmark = aBookmark; + changeData.isAnnotation = true; + changeData.updateLastModified = !aDontUpdateLastModified; + changeData.source = aSource; + changeData.property = aName; + + nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); + if (bookmarks) { + bookmarks->NotifyItemChanged(changeData); + } +} + +} + namespace mozilla { namespace places { @@ -154,6 +180,7 @@ nsAnnotationService::Init() nsresult nsAnnotationService::SetAnnotationStringInternal(nsIURI* aURI, int64_t aItemId, + BookmarkData* aBookmark, const nsACString& aName, const nsAString& aValue, int32_t aFlags, @@ -161,10 +188,13 @@ nsAnnotationService::SetAnnotationStringInternal(nsIURI* aURI, { mozStorageTransaction transaction(mDB->MainConn(), false); nsCOMPtr statement; - nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration, + + nsresult rv = StartSetAnnotation(aURI, aItemId, aBookmark, aName, aFlags, + aExpiration, nsIAnnotationService::TYPE_STRING, statement); NS_ENSURE_SUCCESS(rv, rv); + mozStorageStatementScoper scoper(statement); rv = statement->BindStringByName(NS_LITERAL_CSTRING("content"), aValue); @@ -356,7 +386,7 @@ nsAnnotationService::SetPageAnnotationString(nsIURI* aURI, { NS_ENSURE_ARG(aURI); - nsresult rv = SetAnnotationStringInternal(aURI, 0, aName, aValue, + nsresult rv = SetAnnotationStringInternal(aURI, 0, nullptr, aName, aValue, aFlags, aExpiration); NS_ENSURE_SUCCESS(rv, rv); @@ -380,11 +410,13 @@ nsAnnotationService::SetItemAnnotationString(int64_t aItemId, if (aExpiration == EXPIRE_WITH_HISTORY) return NS_ERROR_INVALID_ARG; - nsresult rv = SetAnnotationStringInternal(nullptr, aItemId, aName, aValue, - aFlags, aExpiration); + BookmarkData bookmark; + nsresult rv = SetAnnotationStringInternal(nullptr, aItemId, &bookmark, aName, + aValue, aFlags, aExpiration); NS_ENSURE_SUCCESS(rv, rv); NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName, aSource, aDontUpdateLastModified)); + NotifyItemChanged(bookmark, aName, aSource, aDontUpdateLastModified); return NS_OK; } @@ -393,6 +425,7 @@ nsAnnotationService::SetItemAnnotationString(int64_t aItemId, nsresult nsAnnotationService::SetAnnotationInt32Internal(nsIURI* aURI, int64_t aItemId, + BookmarkData* aBookmark, const nsACString& aName, int32_t aValue, int32_t aFlags, @@ -400,10 +433,12 @@ nsAnnotationService::SetAnnotationInt32Internal(nsIURI* aURI, { mozStorageTransaction transaction(mDB->MainConn(), false); nsCOMPtr statement; - nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration, + nsresult rv = StartSetAnnotation(aURI, aItemId, aBookmark, aName, aFlags, + aExpiration, nsIAnnotationService::TYPE_INT32, statement); NS_ENSURE_SUCCESS(rv, rv); + mozStorageStatementScoper scoper(statement); rv = statement->BindInt32ByName(NS_LITERAL_CSTRING("content"), aValue); @@ -428,7 +463,7 @@ nsAnnotationService::SetPageAnnotationInt32(nsIURI* aURI, { NS_ENSURE_ARG(aURI); - nsresult rv = SetAnnotationInt32Internal(aURI, 0, aName, aValue, + nsresult rv = SetAnnotationInt32Internal(aURI, 0, nullptr, aName, aValue, aFlags, aExpiration); NS_ENSURE_SUCCESS(rv, rv); @@ -452,11 +487,13 @@ nsAnnotationService::SetItemAnnotationInt32(int64_t aItemId, if (aExpiration == EXPIRE_WITH_HISTORY) return NS_ERROR_INVALID_ARG; - nsresult rv = SetAnnotationInt32Internal(nullptr, aItemId, aName, aValue, - aFlags, aExpiration); + BookmarkData bookmark; + nsresult rv = SetAnnotationInt32Internal(nullptr, aItemId, &bookmark, aName, + aValue, aFlags, aExpiration); NS_ENSURE_SUCCESS(rv, rv); NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName, aSource, aDontUpdateLastModified)); + NotifyItemChanged(bookmark, aName, aSource, aDontUpdateLastModified); return NS_OK; } @@ -465,6 +502,7 @@ nsAnnotationService::SetItemAnnotationInt32(int64_t aItemId, nsresult nsAnnotationService::SetAnnotationInt64Internal(nsIURI* aURI, int64_t aItemId, + BookmarkData* aBookmark, const nsACString& aName, int64_t aValue, int32_t aFlags, @@ -472,10 +510,12 @@ nsAnnotationService::SetAnnotationInt64Internal(nsIURI* aURI, { mozStorageTransaction transaction(mDB->MainConn(), false); nsCOMPtr statement; - nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration, + nsresult rv = StartSetAnnotation(aURI, aItemId, aBookmark, aName, aFlags, + aExpiration, nsIAnnotationService::TYPE_INT64, statement); NS_ENSURE_SUCCESS(rv, rv); + mozStorageStatementScoper scoper(statement); rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("content"), aValue); @@ -500,7 +540,7 @@ nsAnnotationService::SetPageAnnotationInt64(nsIURI* aURI, { NS_ENSURE_ARG(aURI); - nsresult rv = SetAnnotationInt64Internal(aURI, 0, aName, aValue, + nsresult rv = SetAnnotationInt64Internal(aURI, 0, nullptr, aName, aValue, aFlags, aExpiration); NS_ENSURE_SUCCESS(rv, rv); @@ -524,11 +564,13 @@ nsAnnotationService::SetItemAnnotationInt64(int64_t aItemId, if (aExpiration == EXPIRE_WITH_HISTORY) return NS_ERROR_INVALID_ARG; - nsresult rv = SetAnnotationInt64Internal(nullptr, aItemId, aName, aValue, - aFlags, aExpiration); + BookmarkData bookmark; + nsresult rv = SetAnnotationInt64Internal(nullptr, aItemId, &bookmark, aName, + aValue, aFlags, aExpiration); NS_ENSURE_SUCCESS(rv, rv); NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName, aSource, aDontUpdateLastModified)); + NotifyItemChanged(bookmark, aName, aSource, aDontUpdateLastModified); return NS_OK; } @@ -537,6 +579,7 @@ nsAnnotationService::SetItemAnnotationInt64(int64_t aItemId, nsresult nsAnnotationService::SetAnnotationDoubleInternal(nsIURI* aURI, int64_t aItemId, + BookmarkData* aBookmark, const nsACString& aName, double aValue, int32_t aFlags, @@ -544,10 +587,12 @@ nsAnnotationService::SetAnnotationDoubleInternal(nsIURI* aURI, { mozStorageTransaction transaction(mDB->MainConn(), false); nsCOMPtr statement; - nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration, + nsresult rv = StartSetAnnotation(aURI, aItemId, aBookmark, aName, aFlags, + aExpiration, nsIAnnotationService::TYPE_DOUBLE, statement); NS_ENSURE_SUCCESS(rv, rv); + mozStorageStatementScoper scoper(statement); rv = statement->BindDoubleByName(NS_LITERAL_CSTRING("content"), aValue); @@ -572,7 +617,7 @@ nsAnnotationService::SetPageAnnotationDouble(nsIURI* aURI, { NS_ENSURE_ARG(aURI); - nsresult rv = SetAnnotationDoubleInternal(aURI, 0, aName, aValue, + nsresult rv = SetAnnotationDoubleInternal(aURI, 0, nullptr, aName, aValue, aFlags, aExpiration); NS_ENSURE_SUCCESS(rv, rv); @@ -596,11 +641,13 @@ nsAnnotationService::SetItemAnnotationDouble(int64_t aItemId, if (aExpiration == EXPIRE_WITH_HISTORY) return NS_ERROR_INVALID_ARG; - nsresult rv = SetAnnotationDoubleInternal(nullptr, aItemId, aName, aValue, - aFlags, aExpiration); + BookmarkData bookmark; + nsresult rv = SetAnnotationDoubleInternal(nullptr, aItemId, &bookmark, + aName, aValue, aFlags, aExpiration); NS_ENSURE_SUCCESS(rv, rv); NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName, aSource, aDontUpdateLastModified)); + NotifyItemChanged(bookmark, aName, aSource, aDontUpdateLastModified); return NS_OK; } @@ -1367,6 +1414,7 @@ nsAnnotationService::ItemHasAnnotation(int64_t aItemId, nsresult nsAnnotationService::RemoveAnnotationInternal(nsIURI* aURI, int64_t aItemId, + BookmarkData* aBookmark, const nsACString& aName) { bool isItemAnnotation = (aItemId > 0); @@ -1404,6 +1452,16 @@ nsAnnotationService::RemoveAnnotationInternal(nsIURI* aURI, rv = statement->Execute(); NS_ENSURE_SUCCESS(rv, rv); + if (isItemAnnotation) { + nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); + if (bookmarks) { + MOZ_ASSERT(aBookmark); + if (NS_FAILED(bookmarks->FetchItemInfo(aItemId, *aBookmark))) { + aBookmark->id = -1; + } + } + } + return NS_OK; } @@ -1414,7 +1472,7 @@ nsAnnotationService::RemovePageAnnotation(nsIURI* aURI, { NS_ENSURE_ARG(aURI); - nsresult rv = RemoveAnnotationInternal(aURI, 0, aName); + nsresult rv = RemoveAnnotationInternal(aURI, 0, nullptr, aName); NS_ENSURE_SUCCESS(rv, rv); NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationRemoved(aURI, aName)); @@ -1430,10 +1488,12 @@ nsAnnotationService::RemoveItemAnnotation(int64_t aItemId, { NS_ENSURE_ARG_MIN(aItemId, 1); - nsresult rv = RemoveAnnotationInternal(nullptr, aItemId, aName); + BookmarkData bookmark; + nsresult rv = RemoveAnnotationInternal(nullptr, aItemId, &bookmark, aName); NS_ENSURE_SUCCESS(rv, rv); NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationRemoved(aItemId, aName, aSource)); + NotifyItemChanged(bookmark, aName, aSource, false); return NS_OK; } @@ -1468,6 +1528,19 @@ nsAnnotationService::RemovePageAnnotations(nsIURI* aURI) NS_IMETHODIMP nsAnnotationService::RemoveItemAnnotations(int64_t aItemId, uint16_t aSource) +{ + nsresult rv = RemoveItemAnnotationsWithoutNotifying(aItemId); + NS_ENSURE_SUCCESS(rv, rv); + + NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationRemoved(aItemId, EmptyCString(), + aSource)); + + return NS_OK; +} + + +nsresult +nsAnnotationService::RemoveItemAnnotationsWithoutNotifying(int64_t aItemId) { NS_ENSURE_ARG_MIN(aItemId, 1); @@ -1484,9 +1557,6 @@ nsAnnotationService::RemoveItemAnnotations(int64_t aItemId, rv = statement->Execute(); NS_ENSURE_SUCCESS(rv, rv); - NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationRemoved(aItemId, EmptyCString(), - aSource)); - return NS_OK; } @@ -1649,6 +1719,14 @@ nsAnnotationService::CopyItemAnnotations(int64_t aSourceItemId, NS_ENSURE_SUCCESS(rv, rv); NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aDestItemId, annoName, aSource, false)); + + nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); + if (bookmarks) { + BookmarkData bookmark; + if (NS_SUCCEEDED(bookmarks->FetchItemInfo(aDestItemId, bookmark))) { + NotifyItemChanged(bookmark, annoName, aSource, false); + } + } } rv = transaction.Commit(); @@ -1838,6 +1916,7 @@ nsAnnotationService::StartGetAnnotation(nsIURI* aURI, nsresult nsAnnotationService::StartSetAnnotation(nsIURI* aURI, int64_t aItemId, + BookmarkData* aBookmark, const nsACString& aName, int32_t aFlags, uint16_t aExpiration, @@ -1875,8 +1954,9 @@ nsAnnotationService::StartSetAnnotation(nsIURI* aURI, stmt = mDB->GetStatement( "SELECT b.id, " "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, " - "a.id, a.dateAdded " + "a.id, a.dateAdded, b.parent, b.type, b.lastModified, b.guid, p.guid " "FROM moz_bookmarks b " + "JOIN moz_bookmarks p ON p.id = b.parent " "LEFT JOIN moz_items_annos a ON a.item_id = b.id " "AND a.anno_attribute_id = nameid " "WHERE b.id = :item_id" @@ -1926,6 +2006,19 @@ nsAnnotationService::StartSetAnnotation(nsIURI* aURI, "VALUES (:id, :fk, :name_id, :content, :flags, " ":expiration, :type, :date_added, :last_modified)" ); + + // Since we're already querying `moz_bookmarks`, we fetch the changed + // bookmark's info here, instead of using `FetchItemInfo`. + MOZ_ASSERT(aBookmark); + aBookmark->id = fkId; + aBookmark->parentId = stmt->AsInt64(4); + aBookmark->type = stmt->AsInt64(5); + + aBookmark->lastModified = static_cast(stmt->AsInt64(6)); + if (NS_FAILED(stmt->GetUTF8String(7, aBookmark->guid)) || + NS_FAILED(stmt->GetUTF8String(8, aBookmark->parentGuid))) { + aBookmark->id = -1; + } } else { aStatement = mDB->GetStatement( diff --git a/toolkit/components/places/nsAnnotationService.h b/toolkit/components/places/nsAnnotationService.h index f1b4921d81ec..5e3b3703a87a 100644 --- a/toolkit/components/places/nsAnnotationService.h +++ b/toolkit/components/places/nsAnnotationService.h @@ -113,6 +113,7 @@ protected: nsresult StartSetAnnotation(nsIURI* aURI, int64_t aItemId, + BookmarkData* aBookmark, const nsACString& aName, int32_t aFlags, uint16_t aExpiration, @@ -121,24 +122,28 @@ protected: nsresult SetAnnotationStringInternal(nsIURI* aURI, int64_t aItemId, + BookmarkData* aBookmark, const nsACString& aName, const nsAString& aValue, int32_t aFlags, uint16_t aExpiration); nsresult SetAnnotationInt32Internal(nsIURI* aURI, int64_t aItemId, + BookmarkData* aBookmark, const nsACString& aName, int32_t aValue, int32_t aFlags, uint16_t aExpiration); nsresult SetAnnotationInt64Internal(nsIURI* aURI, int64_t aItemId, + BookmarkData* aBookmark, const nsACString& aName, int64_t aValue, int32_t aFlags, uint16_t aExpiration); nsresult SetAnnotationDoubleInternal(nsIURI* aURI, int64_t aItemId, + BookmarkData* aBookmark, const nsACString& aName, double aValue, int32_t aFlags, @@ -146,6 +151,7 @@ protected: nsresult RemoveAnnotationInternal(nsIURI* aURI, int64_t aItemId, + BookmarkData* aBookmark, const nsACString& aName); public: @@ -156,6 +162,7 @@ public: nsresult GetAnnotationNamesTArray(nsIURI* aURI, int64_t aItemId, nsTArray* _result); + nsresult RemoveItemAnnotationsWithoutNotifying(int64_t aItemId); }; #endif /* nsAnnotationService_h___ */ diff --git a/toolkit/components/places/nsIAnnotationService.idl b/toolkit/components/places/nsIAnnotationService.idl index a344319f076f..7f72418727ac 100644 --- a/toolkit/components/places/nsIAnnotationService.idl +++ b/toolkit/components/places/nsIAnnotationService.idl @@ -112,7 +112,8 @@ interface nsIAnnotationService : nsISupports * * For item annotations, aSource should be a change source constant from * nsINavBookmarksService::SOURCE_*, and defaults to SOURCE_DEFAULT if - * omitted. + * omitted. Setting an item annotation also notifies + * `nsINavBookmarkObserver::onItemChanged` for the affected item. * * The annotation "favicon" is special. Favicons are stored in the favicon * service, but are special cased in the protocol handler so they look like @@ -340,6 +341,9 @@ interface nsIAnnotationService : nsISupports /** * Removes a specific annotation. Succeeds even if the annotation is * not found. + * + * Removing an item annotation also notifies + * `nsINavBookmarkObserver::onItemChanged` for the affected item. */ void removePageAnnotation(in nsIURI aURI, in AUTF8String aName); @@ -351,6 +355,9 @@ interface nsIAnnotationService : nsISupports * Removes all annotations for the given page/item. * We may want some other similar functions to get annotations with given * flags (once we have flags defined). + * + * Unlike the other item methods, `removeItemAnnotations` does *not* notify + * `nsINavBookmarkObserver::onItemChanged` for the affected item. */ void removePageAnnotations(in nsIURI aURI); void removeItemAnnotations(in long long aItemId, diff --git a/toolkit/components/places/nsNavBookmarks.cpp b/toolkit/components/places/nsNavBookmarks.cpp index 726cf1e87208..336997eb548e 100644 --- a/toolkit/components/places/nsNavBookmarks.cpp +++ b/toolkit/components/places/nsNavBookmarks.cpp @@ -184,7 +184,6 @@ nsNavBookmarks::~nsNavBookmarks() NS_IMPL_ISUPPORTS(nsNavBookmarks , nsINavBookmarksService , nsINavHistoryObserver -, nsIAnnotationObserver , nsIObserver , nsISupportsWeakReference ) @@ -209,17 +208,11 @@ nsNavBookmarks::Init() nsCOMPtr os = mozilla::services::GetObserverService(); if (os) { - (void)os->AddObserver(this, TOPIC_PLACES_SHUTDOWN, true); (void)os->AddObserver(this, TOPIC_PLACES_CONNECTION_CLOSED, true); } mCanNotify = true; - // Observe annotations. - nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); - NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY); - annosvc->AddObserver(this); - // Allows us to notify on title changes. MUST BE LAST so it is impossible // to fail after this call, or the history service will have a reference to // us and we won't go away. @@ -687,13 +680,15 @@ nsNavBookmarks::RemoveItem(int64_t aItemId, uint16_t aSource) mozStorageTransaction transaction(mDB->MainConn(), false); - // First, if not a tag, remove item annotations. + // First, if not a tag, remove item annotations. We remove annos without + // notifying to avoid firing `onItemAnnotationRemoved` for an item that + // we're about to remove. int64_t tagsRootId = TagsRootId(); bool isUntagging = bookmark.grandParentId == tagsRootId; if (bookmark.parentId != tagsRootId && !isUntagging) { nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY); - rv = annosvc->RemoveItemAnnotations(bookmark.id, aSource); + rv = annosvc->RemoveItemAnnotationsWithoutNotifying(bookmark.id); NS_ENSURE_SUCCESS(rv, rv); } @@ -3092,23 +3087,28 @@ nsNavBookmarks::NotifyItemChanged(const ItemChangeData& aData) { // A guid must always be defined. MOZ_ASSERT(!aData.bookmark.guid.IsEmpty()); + + PRTime lastModified = aData.bookmark.lastModified; + if (aData.updateLastModified) { + lastModified = RoundedPRNow(); + MOZ_ALWAYS_SUCCEEDS(SetItemDateInternal( + LAST_MODIFIED, DetermineSyncChangeDelta(aData.source), + aData.bookmark.id, lastModified)); + } + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemChanged(aData.bookmark.id, aData.property, aData.isAnnotation, aData.newValue, - aData.bookmark.lastModified, + lastModified, aData.bookmark.type, aData.bookmark.parentId, aData.bookmark.guid, aData.bookmark.parentGuid, aData.oldValue, - // We specify the default source here because - // this method is only called for history - // visits, and we don't track sources in - // history. - SOURCE_DEFAULT)); + aData.source)); } //////////////////////////////////////////////////////////////////////////////// @@ -3120,14 +3120,7 @@ nsNavBookmarks::Observe(nsISupports *aSubject, const char *aTopic, { NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread"); - if (strcmp(aTopic, TOPIC_PLACES_SHUTDOWN) == 0) { - // Stop Observing annotations. - nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); - if (annosvc) { - annosvc->RemoveObserver(this); - } - } - else if (strcmp(aTopic, TOPIC_PLACES_CONNECTION_CLOSED) == 0) { + if (strcmp(aTopic, TOPIC_PLACES_CONNECTION_CLOSED) == 0) { // Don't even try to notify observers from this point on, the category // cache would init services that could try to use our APIs. mCanNotify = false; @@ -3306,63 +3299,3 @@ nsNavBookmarks::OnDeleteVisits(nsIURI* aURI, PRTime aVisitTime, } return NS_OK; } - - -// nsIAnnotationObserver - -NS_IMETHODIMP -nsNavBookmarks::OnPageAnnotationSet(nsIURI* aPage, const nsACString& aName) -{ - return NS_OK; -} - - -NS_IMETHODIMP -nsNavBookmarks::OnItemAnnotationSet(int64_t aItemId, const nsACString& aName, - uint16_t aSource, bool aDontUpdateLastModified) -{ - BookmarkData bookmark; - nsresult rv = FetchItemInfo(aItemId, bookmark); - NS_ENSURE_SUCCESS(rv, rv); - - if (!aDontUpdateLastModified) { - bookmark.lastModified = RoundedPRNow(); - rv = SetItemDateInternal(LAST_MODIFIED, DetermineSyncChangeDelta(aSource), - bookmark.id, bookmark.lastModified); - NS_ENSURE_SUCCESS(rv, rv); - } - - NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, - nsINavBookmarkObserver, - OnItemChanged(bookmark.id, - aName, - true, - EmptyCString(), - bookmark.lastModified, - bookmark.type, - bookmark.parentId, - bookmark.guid, - bookmark.parentGuid, - EmptyCString(), - aSource)); - return NS_OK; -} - - -NS_IMETHODIMP -nsNavBookmarks::OnPageAnnotationRemoved(nsIURI* aPage, const nsACString& aName) -{ - return NS_OK; -} - - -NS_IMETHODIMP -nsNavBookmarks::OnItemAnnotationRemoved(int64_t aItemId, const nsACString& aName, - uint16_t aSource) -{ - // As of now this is doing the same as OnItemAnnotationSet, so just forward - // the call. - nsresult rv = OnItemAnnotationSet(aItemId, aName, aSource, false); - NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; -} diff --git a/toolkit/components/places/nsNavBookmarks.h b/toolkit/components/places/nsNavBookmarks.h index 85b01bc44dcf..5d7203b2a6e8 100644 --- a/toolkit/components/places/nsNavBookmarks.h +++ b/toolkit/components/places/nsNavBookmarks.h @@ -7,7 +7,6 @@ #define nsNavBookmarks_h_ #include "nsINavBookmarksService.h" -#include "nsIAnnotationService.h" #include "nsITransaction.h" #include "nsNavHistory.h" #include "nsToolkitCompsCID.h" @@ -28,18 +27,18 @@ namespace places { }; struct BookmarkData { - int64_t id; + int64_t id = -1; nsCString url; nsCString title; - int32_t position; - int64_t placeId; - int64_t parentId; - int64_t grandParentId; - int32_t type; - int32_t syncStatus; + int32_t position = -1; + int64_t placeId = -1; + int64_t parentId = -1; + int64_t grandParentId = -1; + int32_t type = 0; + int32_t syncStatus = nsINavBookmarksService::SYNC_STATUS_UNKNOWN; nsCString serviceCID; - PRTime dateAdded; - PRTime lastModified; + PRTime dateAdded = 0; + PRTime lastModified = 0; nsCString guid; nsCString parentGuid; }; @@ -53,8 +52,10 @@ namespace places { struct ItemChangeData { BookmarkData bookmark; + bool isAnnotation = false; + bool updateLastModified = false; + uint16_t source = nsINavBookmarksService::SOURCE_DEFAULT; nsCString property; - bool isAnnotation; nsCString newValue; nsCString oldValue; }; @@ -77,7 +78,6 @@ namespace places { class nsNavBookmarks final : public nsINavBookmarksService , public nsINavHistoryObserver - , public nsIAnnotationObserver , public nsIObserver , public nsSupportsWeakReference { @@ -85,7 +85,6 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSINAVBOOKMARKSSERVICE NS_DECL_NSINAVHISTORYOBSERVER - NS_DECL_NSIANNOTATIONOBSERVER NS_DECL_NSIOBSERVER nsNavBookmarks(); diff --git a/toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js b/toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js index 3a8c3b323ef6..cc4898288a32 100644 --- a/toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js +++ b/toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js @@ -231,8 +231,6 @@ add_task(async function remove_bookmark() { let observer = expectNotifications(); await PlacesUtils.bookmarks.remove(bm.guid); - // TODO (Bug 653910): onItemAnnotationRemoved notified even if there were no - // annotations. observer.check([ { name: "onItemRemoved", arguments: [ itemId, parentId, bm.index, bm.type, bm.url, bm.guid, bm.parentGuid, @@ -254,8 +252,6 @@ add_task(async function remove_multiple_bookmarks() { let observer = expectNotifications(); await PlacesUtils.bookmarks.remove([bm1, bm2]); - // TODO (Bug 653910): onItemAnnotationRemoved notified even if there were no - // annotations. observer.check([ { name: "onItemRemoved", arguments: [ itemId1, parentId1, bm1.index, bm1.type, bm1.url, bm1.guid, bm1.parentGuid, diff --git a/toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js b/toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js index e68ce290e001..4e30ee04c6f9 100644 --- a/toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js +++ b/toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js @@ -211,7 +211,7 @@ add_task(async function onItemChanged_title_bookmark() { "onItemChanged" ]), gBookmarksObserver.setup([ - { name: "onItemChanged", // This is an unfortunate effect of bug 653910. + { name: "onItemChanged", args: [ { name: "itemId", check: v => typeof(v) == "number" && v > 0 }, { name: "property", check: v => v === "title" }, @@ -393,23 +393,9 @@ add_task(async function onItemRemoved_bookmark() { let uri = Services.io.newURI(url); let promise = Promise.all([ gBookmarkSkipObserver.setup([ - "onItemChanged", "onItemRemoved" + "onItemRemoved" ]), gBookmarksObserver.setup([ - { name: "onItemChanged", // This is an unfortunate effect of bug 653910. - args: [ - { name: "itemId", check: v => typeof(v) == "number" && v > 0 }, - { name: "property", check: v => v === "" }, - { name: "isAnno", check: v => v === true }, - { name: "newValue", check: v => v === "" }, - { name: "lastModified", check: v => typeof(v) == "number" && v > 0 }, - { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK }, - { name: "parentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId }, - { name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) }, - { name: "parentGuid", check: v => typeof(v) == "string" && GUID_RE.test(v) }, - { name: "oldValue", check: v => typeof(v) == "string" }, - { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) }, - ] }, { name: "onItemRemoved", args: [ { name: "itemId", check: v => typeof(v) == "number" && v > 0 }, @@ -430,23 +416,9 @@ add_task(async function onItemRemoved_separator() { let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId, 0); let promise = Promise.all([ gBookmarkSkipObserver.setup([ - "onItemChanged", "onItemRemoved" + "onItemRemoved" ]), gBookmarksObserver.setup([ - { name: "onItemChanged", // This is an unfortunate effect of bug 653910. - args: [ - { name: "itemId", check: v => typeof(v) == "number" && v > 0 }, - { name: "property", check: v => v === "" }, - { name: "isAnno", check: v => v === true }, - { name: "newValue", check: v => v === "" }, - { name: "lastModified", check: v => typeof(v) == "number" && v > 0 }, - { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_SEPARATOR }, - { name: "parentId", check: v => typeof(v) == "number" && v > 0 }, - { name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) }, - { name: "parentGuid", check: v => typeof(v) == "string" && GUID_RE.test(v) }, - { name: "oldValue", check: v => typeof(v) == "string" }, - { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) }, - ] }, { name: "onItemRemoved", args: [ { name: "itemId", check: v => typeof(v) == "number" && v > 0 }, @@ -467,23 +439,9 @@ add_task(async function onItemRemoved_folder() { let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId, 0); let promise = Promise.all([ gBookmarkSkipObserver.setup([ - "onItemChanged", "onItemRemoved" + "onItemRemoved" ]), gBookmarksObserver.setup([ - { name: "onItemChanged", // This is an unfortunate effect of bug 653910. - args: [ - { name: "itemId", check: v => typeof(v) == "number" && v > 0 }, - { name: "property", check: v => v === "" }, - { name: "isAnno", check: v => v === true }, - { name: "newValue", check: v => v === "" }, - { name: "lastModified", check: v => typeof(v) == "number" && v > 0 }, - { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_FOLDER }, - { name: "parentId", check: v => typeof(v) == "number" && v > 0 }, - { name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) }, - { name: "parentGuid", check: v => typeof(v) == "string" && GUID_RE.test(v) }, - { name: "oldValue", check: v => typeof(v) == "string" }, - { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) }, - ] }, { name: "onItemRemoved", args: [ { name: "itemId", check: v => typeof(v) == "number" && v > 0 }, @@ -507,7 +465,7 @@ add_task(async function onItemRemoved_folder_recursive() { let promise = Promise.all([ gBookmarkSkipObserver.setup([ "onItemAdded", "onItemAdded", "onItemAdded", "onItemAdded", - "onItemChanged", "onItemRemoved" + "onItemRemoved" ]), gBookmarksObserver.setup([ { name: "onItemAdded", @@ -562,20 +520,6 @@ add_task(async function onItemRemoved_folder_recursive() { { name: "parentGuid", check: v => typeof(v) == "string" && GUID_RE.test(v) }, { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) }, ] }, - { name: "onItemChanged", // This is an unfortunate effect of bug 653910. - args: [ - { name: "itemId", check: v => typeof(v) == "number" && v > 0 }, - { name: "property", check: v => v === "" }, - { name: "isAnno", check: v => v === true }, - { name: "newValue", check: v => v === "" }, - { name: "lastModified", check: v => typeof(v) == "number" && v > 0 }, - { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_FOLDER }, - { name: "parentId", check: v => typeof(v) == "number" && v > 0 }, - { name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) }, - { name: "parentGuid", check: v => typeof(v) == "string" && GUID_RE.test(v) }, - { name: "oldValue", check: v => typeof(v) == "string" }, - { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) }, - ] }, { name: "onItemRemoved", args: [ { name: "itemId", check: v => typeof(v) == "number" && v > 0 }, diff --git a/toolkit/components/places/tests/unit/test_null_interfaces.js b/toolkit/components/places/tests/unit/test_null_interfaces.js index 112cd97633e2..4b98c4da2a7d 100644 --- a/toolkit/components/places/tests/unit/test_null_interfaces.js +++ b/toolkit/components/places/tests/unit/test_null_interfaces.js @@ -14,9 +14,8 @@ var testServices = [ ["queryStringToQueries", "removePagesByTimeframe", "removePagesFromHost", "getObservers"] ], ["browser/nav-bookmarks-service;1", - ["nsINavBookmarksService", "nsINavHistoryObserver", "nsIAnnotationObserver"], - ["createFolder", "getObservers", "onFrecencyChanged", "onTitleChanged", - "onPageAnnotationSet", "onPageAnnotationRemoved", "onDeleteURI"] + ["nsINavBookmarksService", "nsINavHistoryObserver"], + ["createFolder", "getObservers", "onFrecencyChanged", "onTitleChanged", "onDeleteURI"] ], ["browser/livemark-service;2", ["mozIAsyncLivemarks"], ["reloadLivemarks"]], ["browser/annotation-service;1", ["nsIAnnotationService"], ["getObservers"]], diff --git a/toolkit/content/tests/browser/browser.ini b/toolkit/content/tests/browser/browser.ini index e8e5df4aba40..433cb88c5a39 100644 --- a/toolkit/content/tests/browser/browser.ini +++ b/toolkit/content/tests/browser/browser.ini @@ -32,6 +32,7 @@ tags = audiochannel [browser_audioCompeting_onlyForActiveAgent.js] tags = audiochannel [browser_autoplay_policy_play_twice.js] +[browser_autoplay_policy_user_gestures.js] [browser_autoscroll_disabled.js] [browser_block_autoplay_media.js] tags = audiochannel diff --git a/toolkit/content/tests/browser/browser_autoplay_policy_user_gestures.js b/toolkit/content/tests/browser/browser_autoplay_policy_user_gestures.js new file mode 100644 index 000000000000..0bd68458c0e9 --- /dev/null +++ b/toolkit/content/tests/browser/browser_autoplay_policy_user_gestures.js @@ -0,0 +1,103 @@ +const FILE = "https://example.com/browser/toolkit/content/tests/browser/gizmo.mp4"; + +var UserGestures = { + MOUSE_CLICK: "mouse-click", + MOUSE_MOVE: "mouse-move", + KEYBOARD_PRESS: "keyboard-press" +}; + +var UserGestureTests = [ + {type: UserGestures.MOUSE_CLICK, isActivationGesture: true}, + {type: UserGestures.MOUSE_MOVE, isActivationGesture: false}, + {type: UserGestures.KEYBOARD_PRESS, isActivationGesture: true} +]; + +function setup_test_preference() { + return SpecialPowers.pushPrefEnv({"set": [ + ["media.autoplay.enabled", false], + ["media.autoplay.enabled.user-gestures-needed", true] + ]}); +} + +function simulateUserGesture(gesture, targetBrowser) { + let targetElement = targetBrowser.contentDocument.documentElement; + info(`- simulate ${gesture.type} event -`); + switch (gesture.type) { + case UserGestures.MOUSE_CLICK: + return BrowserTestUtils.synthesizeMouseAtCenter(targetElement, {button: 0}, + targetBrowser); + case UserGestures.MOUSE_MOVE: + return BrowserTestUtils.synthesizeMouseAtCenter(targetElement, {type: "mousemove"}, + targetBrowser); + case UserGestures.KEYBOARD_PRESS: + return BrowserTestUtils.sendChar("a", targetBrowser); + default: + ok(false, "undefined user gesture"); + return false; + } +} + +async function test_play_without_user_gesture() { + info("- open new tab -"); + let tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser, + "about:blank"); + info("- create autoplay video -"); + let document = tab.linkedBrowser.contentDocument; + let video = document.createElement("video"); + video.src = FILE; + video.autoplay = true; + let canplayPromise = once(video, "canplaythrough"); + document.body.appendChild(video); + + info("- can't autoplay without user activation -"); + await canplayPromise; + ok(video.paused, "video can't start without user input."); + + info("- call play() without user activation -"); + await video.play().catch(function() { + ok(video.paused, "video can't start play without user input."); + }); + + info("- remove tab -"); + await BrowserTestUtils.removeTab(tab); +} + +async function test_play_with_user_gesture(gesture) { + info("- open new tab -"); + let tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser, + "about:blank"); + info("- create autoplay video -"); + let document = tab.linkedBrowser.contentDocument; + let video = document.createElement("video"); + video.src = FILE; + document.body.appendChild(video); + + info("- simulate user gesture -"); + await simulateUserGesture(gesture, tab.linkedBrowser); + + info("- call play() -"); + try { + await video.play(); + ok(gesture.isActivationGesture, "user gesture can activate the page"); + ok(!video.paused, "video starts playing."); + } catch (e) { + ok(!gesture.isActivationGesture, "user gesture can not activate the page"); + ok(video.paused, "video can not start playing."); + } + + info("- remove tab -"); + await BrowserTestUtils.removeTab(tab); +} + +add_task(async function start_test() { + info("- setup test preference -"); + await setup_test_preference(); + + info("- test play when page doesn't be activated -"); + await test_play_without_user_gesture(); + + info("- test play after page got user gesture -"); + for (let idx = 0; idx < UserGestureTests.length; idx++) { + await test_play_with_user_gesture(UserGestureTests[idx]); + } +}); diff --git a/toolkit/mozapps/installer/upload-files.mk b/toolkit/mozapps/installer/upload-files.mk index 9769f613589d..c356334bbd7d 100644 --- a/toolkit/mozapps/installer/upload-files.mk +++ b/toolkit/mozapps/installer/upload-files.mk @@ -396,15 +396,15 @@ UPLOAD_FILES= \ $(call QUOTED_WILDCARD,$(MOZ_TEST_PACKAGES_FILE)) \ $(call QUOTED_WILDCARD,$(PKG_JSSHELL)) \ $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip) \ - $(call QUOTED_WILDCARD,$(topobjdir)/browser/installer/windows/instgen/setup.exe) \ - $(call QUOTED_WILDCARD,$(topobjdir)/browser/installer/windows/instgen/setup-stub.exe) \ + $(call QUOTED_WILDCARD,$(topobjdir)/$(MOZ_BUILD_APP)/installer/windows/instgen/setup.exe) \ + $(call QUOTED_WILDCARD,$(topobjdir)/$(MOZ_BUILD_APP)/installer/windows/instgen/setup-stub.exe) \ $(call QUOTED_WILDCARD,$(topsrcdir)/toolchains.json) \ $(if $(UPLOAD_EXTRA_FILES), $(foreach f, $(UPLOAD_EXTRA_FILES), $(wildcard $(DIST)/$(f)))) ifneq ($(filter-out en-US x-test,$(AB_CD)),) UPLOAD_FILES += \ - $(call QUOTED_WILDCARD,$(topobjdir)/browser/installer/windows/l10ngen/setup.exe) \ - $(call QUOTED_WILDCARD,$(topobjdir)/browser/installer/windows/l10ngen/setup-stub.exe) + $(call QUOTED_WILDCARD,$(topobjdir)/$(MOZ_BUILD_APP)/installer/windows/l10ngen/setup.exe) \ + $(call QUOTED_WILDCARD,$(topobjdir)/$(MOZ_BUILD_APP)/installer/windows/l10ngen/setup-stub.exe) endif diff --git a/tools/lint/eslint/modules.json b/tools/lint/eslint/modules.json index 17e42d7b300a..86b698171513 100644 --- a/tools/lint/eslint/modules.json +++ b/tools/lint/eslint/modules.json @@ -152,7 +152,7 @@ "passwords.jsm": ["Password", "DumpPasswords"], "PdfJsNetwork.jsm": ["NetworkManager"], "PhoneNumberMetaData.jsm": ["PHONE_NUMBER_META_DATA"], - "PlacesUtils.jsm": ["PlacesUtils", "PlacesAggregatedTransaction", "PlacesCreateFolderTransaction", "PlacesCreateBookmarkTransaction", "PlacesCreateSeparatorTransaction", "PlacesCreateLivemarkTransaction", "PlacesMoveItemTransaction", "PlacesRemoveItemTransaction", "PlacesEditItemTitleTransaction", "PlacesEditBookmarkURITransaction", "PlacesEditLivemarkFeedURITransaction", "PlacesEditLivemarkSiteURITransaction", "PlacesSetItemAnnotationTransaction", "PlacesSetPageAnnotationTransaction", "PlacesEditBookmarkKeywordTransaction", "PlacesEditBookmarkPostDataTransaction", "PlacesEditItemDateAddedTransaction", "PlacesEditItemLastModifiedTransaction", "PlacesSortFolderByNameTransaction", "PlacesTagURITransaction", "PlacesUntagURITransaction"], + "PlacesUtils.jsm": ["PlacesUtils", "PlacesAggregatedTransaction", "PlacesCreateFolderTransaction", "PlacesCreateBookmarkTransaction", "PlacesCreateSeparatorTransaction", "PlacesCreateLivemarkTransaction", "PlacesMoveItemTransaction", "PlacesRemoveItemTransaction", "PlacesEditItemTitleTransaction", "PlacesEditBookmarkURITransaction", "PlacesSetItemAnnotationTransaction", "PlacesSetPageAnnotationTransaction", "PlacesEditBookmarkKeywordTransaction", "PlacesEditBookmarkPostDataTransaction", "PlacesEditItemDateAddedTransaction", "PlacesEditItemLastModifiedTransaction", "PlacesSortFolderByNameTransaction", "PlacesTagURITransaction", "PlacesUntagURITransaction"], "PluginProvider.jsm": [], "PointerAdapter.jsm": ["PointerRelay", "PointerAdapter"], "policies.js": ["ErrorHandler", "SyncScheduler"], diff --git a/widget/CommandList.h b/widget/CommandList.h index 2bd327c86e22..7fcea8713af8 100644 --- a/widget/CommandList.h +++ b/widget/CommandList.h @@ -4,9 +4,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * Define NS_DEFIVE_COMMAND(aName, aCommandStr) before including this. + * Define NS_DEFINE_COMMAND(aName, aCommandStr) before including this. * @param aName The name useful in C++ of the command. * @param aCommandStr The command string in JS. + * + * Define NS_DEFINE_COMMAND_NO_EXEC_COMMAND(aName) before including this. + * @param aName The name useful in C++ of the command. */ NS_DEFINE_COMMAND(BeginLine, cmd_beginLine) @@ -22,6 +25,8 @@ NS_DEFINE_COMMAND(DeleteToEndOfLine, cmd_deleteToEndOfLine) NS_DEFINE_COMMAND(DeleteWordBackward, cmd_deleteWordBackward) NS_DEFINE_COMMAND(DeleteWordForward, cmd_deleteWordForward) NS_DEFINE_COMMAND(EndLine, cmd_endLine) +NS_DEFINE_COMMAND(InsertParagraph, cmd_insertParagraph) +NS_DEFINE_COMMAND(InsertLineBreak, cmd_insertLineBreak) NS_DEFINE_COMMAND(LineNext, cmd_lineNext) NS_DEFINE_COMMAND(LinePrevious, cmd_linePrevious) NS_DEFINE_COMMAND(MoveBottom, cmd_moveBottom) @@ -50,3 +55,8 @@ NS_DEFINE_COMMAND(SelectWordNext, cmd_selectWordNext) NS_DEFINE_COMMAND(SelectWordPrevious, cmd_selectWordPrevious) NS_DEFINE_COMMAND(WordNext, cmd_wordNext) NS_DEFINE_COMMAND(WordPrevious, cmd_wordPrevious) + +NS_DEFINE_COMMAND_NO_EXEC_COMMAND(CancelOperation) +NS_DEFINE_COMMAND_NO_EXEC_COMMAND(Complete) +NS_DEFINE_COMMAND_NO_EXEC_COMMAND(InsertBacktab) +NS_DEFINE_COMMAND_NO_EXEC_COMMAND(InsertTab) diff --git a/widget/EventForwards.h b/widget/EventForwards.h index 1e9342d6df1c..ed2496750b0d 100644 --- a/widget/EventForwards.h +++ b/widget/EventForwards.h @@ -114,6 +114,7 @@ enum CodeNameIndex : CodeNameIndexType const nsCString ToString(CodeNameIndex aCodeNameIndex); #define NS_DEFINE_COMMAND(aName, aCommandStr) , Command##aName +#define NS_DEFINE_COMMAND_NO_EXEC_COMMAND(aName) , Command##aName typedef int8_t CommandInt; enum Command : CommandInt @@ -123,6 +124,9 @@ enum Command : CommandInt #include "mozilla/CommandList.h" }; #undef NS_DEFINE_COMMAND +#undef NS_DEFINE_COMMAND_NO_EXEC_COMMAND + +const char* ToChar(Command aCommand); } // namespace mozilla diff --git a/widget/WidgetEventImpl.cpp b/widget/WidgetEventImpl.cpp index f1541641cc3f..bff041c88685 100644 --- a/widget/WidgetEventImpl.cpp +++ b/widget/WidgetEventImpl.cpp @@ -85,6 +85,32 @@ ToString(CodeNameIndex aCodeNameIndex) return NS_ConvertUTF16toUTF8(codeName); } +const char* +ToChar(Command aCommand) +{ + if (aCommand == CommandDoNothing) { + return "CommandDoNothing"; + } + + switch (aCommand) { + +#define NS_DEFINE_COMMAND(aName, aCommandStr) \ + case Command##aName: \ + return "Command" #aName; +#define NS_DEFINE_COMMAND_NO_EXEC_COMMAND(aName) \ + case Command##aName: \ + return "Command" #aName; + +#include "mozilla/CommandList.h" + +#undef NS_DEFINE_COMMAND +#undef NS_DEFINE_COMMAND_NO_EXEC_COMMAND + + default: + return "illegal command value"; + } +} + const nsCString GetDOMKeyCodeName(uint32_t aKeyCode) { @@ -1142,11 +1168,13 @@ WidgetKeyboardEvent::GetCodeNameIndex(const nsAString& aCodeValue) WidgetKeyboardEvent::GetCommandStr(Command aCommand) { #define NS_DEFINE_COMMAND(aName, aCommandStr) , #aCommandStr +#define NS_DEFINE_COMMAND_NO_EXEC_COMMAND(aName) static const char* const kCommands[] = { "" // CommandDoNothing #include "mozilla/CommandList.h" }; #undef NS_DEFINE_COMMAND +#undef NS_DEFINE_COMMAND_NO_EXEC_COMMAND MOZ_RELEASE_ASSERT(static_cast(aCommand) < ArrayLength(kCommands), "Illegal command enumeration value"); diff --git a/widget/cocoa/TextInputHandler.h b/widget/cocoa/TextInputHandler.h index 2ce50b7d6096..1e41c3752822 100644 --- a/widget/cocoa/TextInputHandler.h +++ b/widget/cocoa/TextInputHandler.h @@ -627,14 +627,141 @@ protected: return !mKeyDownHandled && !mKeyPressHandled; } - bool IsEnterKeyEvent() const + bool IsProperKeyEvent(Command aCommand) const { if (NS_WARN_IF(!mKeyEvent)) { return false; } KeyNameIndex keyNameIndex = TISInputSourceWrapper::ComputeGeckoKeyNameIndex([mKeyEvent keyCode]); - return keyNameIndex == KEY_NAME_INDEX_Enter; + Modifiers modifiers = + nsCocoaUtils::ModifiersForEvent(mKeyEvent) & (MODIFIER_SHIFT | + MODIFIER_CONTROL | + MODIFIER_ALT | + MODIFIER_META); + switch (aCommand) { + case CommandInsertLineBreak: + return keyNameIndex == KEY_NAME_INDEX_Enter && + modifiers == MODIFIER_CONTROL; + case CommandInsertParagraph: + return keyNameIndex == KEY_NAME_INDEX_Enter && + modifiers == MODIFIER_NONE; + case CommandDeleteCharBackward: + return keyNameIndex == KEY_NAME_INDEX_Backspace && + modifiers == MODIFIER_NONE; + case CommandDeleteToBeginningOfLine: + return keyNameIndex == KEY_NAME_INDEX_Backspace && + modifiers == MODIFIER_META; + case CommandDeleteWordBackward: + return keyNameIndex == KEY_NAME_INDEX_Backspace && + modifiers == MODIFIER_ALT; + case CommandDeleteCharForward: + return keyNameIndex == KEY_NAME_INDEX_Delete && + modifiers == MODIFIER_NONE; + case CommandDeleteWordForward: + return keyNameIndex == KEY_NAME_INDEX_Delete && + modifiers == MODIFIER_ALT; + case CommandInsertTab: + return keyNameIndex == KEY_NAME_INDEX_Tab && + modifiers == MODIFIER_NONE; + case CommandInsertBacktab: + return keyNameIndex == KEY_NAME_INDEX_Tab && + modifiers == MODIFIER_SHIFT; + case CommandCharNext: + return keyNameIndex == KEY_NAME_INDEX_ArrowRight && + modifiers == MODIFIER_NONE; + case CommandSelectCharNext: + return keyNameIndex == KEY_NAME_INDEX_ArrowRight && + modifiers == MODIFIER_SHIFT; + case CommandWordNext: + return keyNameIndex == KEY_NAME_INDEX_ArrowRight && + modifiers == MODIFIER_ALT; + case CommandSelectWordNext: + return keyNameIndex == KEY_NAME_INDEX_ArrowRight && + modifiers == (MODIFIER_ALT | MODIFIER_SHIFT); + case CommandEndLine: + return keyNameIndex == KEY_NAME_INDEX_ArrowRight && + modifiers == MODIFIER_META; + case CommandSelectEndLine: + return keyNameIndex == KEY_NAME_INDEX_ArrowRight && + modifiers == (MODIFIER_META | MODIFIER_SHIFT); + case CommandCharPrevious: + return keyNameIndex == KEY_NAME_INDEX_ArrowLeft && + modifiers == MODIFIER_NONE; + case CommandSelectCharPrevious: + return keyNameIndex == KEY_NAME_INDEX_ArrowLeft && + modifiers == MODIFIER_SHIFT; + case CommandWordPrevious: + return keyNameIndex == KEY_NAME_INDEX_ArrowLeft && + modifiers == MODIFIER_ALT; + case CommandSelectWordPrevious: + return keyNameIndex == KEY_NAME_INDEX_ArrowLeft && + modifiers == (MODIFIER_ALT | MODIFIER_SHIFT); + case CommandBeginLine: + return keyNameIndex == KEY_NAME_INDEX_ArrowLeft && + modifiers == MODIFIER_META; + case CommandSelectBeginLine: + return keyNameIndex == KEY_NAME_INDEX_ArrowLeft && + modifiers == (MODIFIER_META | MODIFIER_SHIFT); + case CommandLinePrevious: + return keyNameIndex == KEY_NAME_INDEX_ArrowUp && + modifiers == MODIFIER_NONE; + case CommandSelectLinePrevious: + return keyNameIndex == KEY_NAME_INDEX_ArrowUp && + modifiers == MODIFIER_SHIFT; + case CommandMoveTop: + return keyNameIndex == KEY_NAME_INDEX_ArrowUp && + modifiers == MODIFIER_META; + case CommandSelectTop: + return (keyNameIndex == KEY_NAME_INDEX_ArrowUp && + modifiers == (MODIFIER_META | MODIFIER_SHIFT)) || + (keyNameIndex == KEY_NAME_INDEX_Home && + modifiers == MODIFIER_SHIFT); + case CommandLineNext: + return keyNameIndex == KEY_NAME_INDEX_ArrowDown && + modifiers == MODIFIER_NONE; + case CommandSelectLineNext: + return keyNameIndex == KEY_NAME_INDEX_ArrowDown && + modifiers == MODIFIER_SHIFT; + case CommandMoveBottom: + return keyNameIndex == KEY_NAME_INDEX_ArrowDown && + modifiers == MODIFIER_META; + case CommandSelectBottom: + return (keyNameIndex == KEY_NAME_INDEX_ArrowDown && + modifiers == (MODIFIER_META | MODIFIER_SHIFT)) || + (keyNameIndex == KEY_NAME_INDEX_End && + modifiers == MODIFIER_SHIFT); + case CommandScrollPageUp: + return keyNameIndex == KEY_NAME_INDEX_PageUp && + modifiers == MODIFIER_NONE; + case CommandSelectPageUp: + return keyNameIndex == KEY_NAME_INDEX_PageUp && + modifiers == MODIFIER_SHIFT; + case CommandScrollPageDown: + return keyNameIndex == KEY_NAME_INDEX_PageDown && + modifiers == MODIFIER_NONE; + case CommandSelectPageDown: + return keyNameIndex == KEY_NAME_INDEX_PageDown && + modifiers == MODIFIER_SHIFT; + case CommandScrollBottom: + return keyNameIndex == KEY_NAME_INDEX_End && + modifiers == MODIFIER_NONE; + case CommandScrollTop: + return keyNameIndex == KEY_NAME_INDEX_Home && + modifiers == MODIFIER_NONE; + case CommandCancelOperation: + return (keyNameIndex == KEY_NAME_INDEX_Escape && + (modifiers == MODIFIER_NONE || + modifiers == MODIFIER_SHIFT)) || + ([mKeyEvent keyCode] == kVK_ANSI_Period && + modifiers == MODIFIER_META); + case CommandComplete: + return keyNameIndex == KEY_NAME_INDEX_Escape && + (modifiers == MODIFIER_ALT || + modifiers == (MODIFIER_ALT | MODIFIER_SHIFT)); + default: + return false; + } } void InitKeyEvent(TextInputHandlerBase* aHandler, @@ -1160,13 +1287,13 @@ public: NSRange* aReplacementRange = nullptr); /** - * Handles "insertNewline:" command. Due to bug 1350541, this always - * dispatches a keypress event of Enter key unless there is composition. - * If it's handling Enter key event, this dispatches "actual" Enter - * keypress event. Otherwise, dispatches "fake" Enter keypress event - * whose code value is unidentified. + * Handles aCommand. This may cause dispatching an eKeyPress event. + * + * @param aCommand The command which receives from Cocoa. + * @return true if this handles the command even if it does + * nothing actually. Otherwise, false. */ - void InsertNewline(); + bool HandleCommand(Command aCommand); /** * doCommandBySelector event handler. diff --git a/widget/cocoa/TextInputHandler.mm b/widget/cocoa/TextInputHandler.mm index 9cd46dbc09f0..2ca14287b954 100644 --- a/widget/cocoa/TextInputHandler.mm +++ b/widget/cocoa/TextInputHandler.mm @@ -2350,23 +2350,23 @@ TextInputHandler::InsertText(NSAttributedString* aAttrString, NS_OBJC_END_TRY_ABORT_BLOCK; } -void -TextInputHandler::InsertNewline() +bool +TextInputHandler::HandleCommand(Command aCommand) { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; if (Destroyed()) { - return; + return false; } KeyEventState* currentKeyEvent = GetCurrentKeyEvent(); MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::InsertNewline, " - "IsIMEComposing()=%s, " + ("%p TextInputHandler::HandleCommand, " + "aCommand=%s, IsIMEComposing()=%s, " "keyevent=%p, keydownHandled=%s, keypressDispatched=%s, " "causedOtherKeyEvents=%s, compositionDispatched=%s", - this, TrueOrFalse(IsIMEComposing()), + this, ToChar(aCommand), TrueOrFalse(IsIMEComposing()), currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr, currentKeyEvent ? TrueOrFalse(currentKeyEvent->mKeyDownHandled) : "N/A", @@ -2377,74 +2377,337 @@ TextInputHandler::InsertNewline() currentKeyEvent ? TrueOrFalse(currentKeyEvent->mCompositionDispatched) : "N/A")); - // If "insertNewline:" command shouldn't be handled, let's ignore it. + // The command shouldn't be handled, let's ignore it. if (currentKeyEvent && !currentKeyEvent->CanHandleCommand()) { - return; + return false; } // If it's in composition, we cannot dispatch keypress event. - // Therefore, we should insert '\n' as committing composition. + // Therefore, we should use different approach or give up to handle + // the command. if (IsIMEComposing()) { - NSAttributedString* lineBreaker = - [[NSAttributedString alloc] initWithString:@"\n"]; - InsertTextAsCommittingComposition(lineBreaker, nullptr); - if (currentKeyEvent) { - currentKeyEvent->mCompositionDispatched = true; + switch (aCommand) { + case CommandInsertLineBreak: + case CommandInsertParagraph: { + // Insert '\n' as committing composition. + // Otherwise, we need to dispatch keypress event because HTMLEditor + // doesn't treat "\n" in composition string as a line break unless + // the whitespace is treated as pre (see bug 1350541). In strictly + // speaking, we should dispatch keypress event as-is if it's handling + // NSKeyDown event or should insert it with committing composition. + NSAttributedString* lineBreaker = + [[NSAttributedString alloc] initWithString:@"\n"]; + InsertTextAsCommittingComposition(lineBreaker, nullptr); + if (currentKeyEvent) { + currentKeyEvent->mCompositionDispatched = true; + } + [lineBreaker release]; + return true; + } + case CommandDeleteCharBackward: + case CommandDeleteCharForward: + case CommandDeleteToBeginningOfLine: + case CommandDeleteWordBackward: + case CommandDeleteWordForward: + // Don't remove any contents during composition. + return false; + case CommandInsertTab: + case CommandInsertBacktab: + // Don't move focus during composition. + return false; + case CommandCharNext: + case CommandSelectCharNext: + case CommandWordNext: + case CommandSelectWordNext: + case CommandEndLine: + case CommandSelectEndLine: + case CommandCharPrevious: + case CommandSelectCharPrevious: + case CommandWordPrevious: + case CommandSelectWordPrevious: + case CommandBeginLine: + case CommandSelectBeginLine: + case CommandLinePrevious: + case CommandSelectLinePrevious: + case CommandMoveTop: + case CommandLineNext: + case CommandSelectLineNext: + case CommandMoveBottom: + case CommandSelectBottom: + case CommandSelectPageUp: + case CommandSelectPageDown: + case CommandScrollBottom: + case CommandScrollTop: + // Don't move selection during composition. + return false; + case CommandCancelOperation: + case CommandComplete: + // Don't handle Escape key by ourselves during composition. + return false; + case CommandScrollPageUp: + case CommandScrollPageDown: + // Allow to scroll. + break; + default: + break; } - [lineBreaker release]; - return; } - // Otherwise, we need to dispatch keypress event because HTMLEditor doesn't - // treat "\n" in composition string as a line break unless the whitespace is - // treated as pre (see bug 1350541). In strictly speaking, we should - // dispatch keypress event as-is if it's handling NSKeyDown event or - // should insert it with committing composition. - RefPtr widget(mWidget); nsresult rv = mDispatcher->BeginNativeInputTransaction(); if (NS_WARN_IF(NS_FAILED(rv))) { MOZ_LOG(gLog, LogLevel::Error, - ("%p, IMEInputHandler::InsertNewline, " + ("%p, IMEInputHandler::HandleCommand, " "FAILED, due to BeginNativeInputTransaction() failure", this)); - return; + return false; } - // TODO: If it's not Enter keypress but user customized the OS settings - // to insert a line breaker with other key, we should just set + // TODO: If it's not appropriate keypress but user customized the OS + // settings to do the command with other key, we should just set // command to the keypress event and it should be handled as - // Enter key press in editor. + // the key press in editor. - // If it's handling actual Enter key event and hasn't cause any composition + // If it's handling actual key event and hasn't cause any composition // events nor other key events, we should expose actual modifier state. - // Otherwise, we should remove Control, Option and Command state since - // editor may behave differently if some of them are active. Although, - // Shift+Enter and Enter are work differently in HTML editor, we should - // expose actual Shift state if it's caused by Enter key for compatibility - // with Chromium. Chromium breaks line in HTML editor with default pargraph - // separator when Enter is pressed, with
                    element when Shift+Enter. - // Safari breaks line in HTML editor with default paragraph separator when - // Enter, Shift+Enter or Option+Enter. So, we should not change Shift+Enter - // meaning when there was composition string or not. + // Otherwise, we should adjust Control, Option and Command state since + // editor may behave differently if some of them are active. bool dispatchFakeKeyPress = - !(currentKeyEvent && currentKeyEvent->IsEnterKeyEvent() && + !(currentKeyEvent && currentKeyEvent->IsProperKeyEvent(aCommand) && currentKeyEvent->CanDispatchKeyPressEvent()); WidgetKeyboardEvent keypressEvent(true, eKeyPress, widget); if (!dispatchFakeKeyPress) { - // If we're acutally handling an Enter key press, we should dispatch - // Enter keypress event as-is. + // If we're acutally handling a key press, we should dispatch + // the keypress event as-is. currentKeyEvent->InitKeyEvent(this, keypressEvent); } else { - // Otherwise, we should dispatch "fake" Enter keypress event. - // In this case, we shouldn't set code value to "Enter". - NSEvent* keyEvent = currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr; - nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent); - keypressEvent.mKeyCode = NS_VK_RETURN; - keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_Enter; - keypressEvent.mModifiers &= ~(MODIFIER_CONTROL | - MODIFIER_ALT | - MODIFIER_META); + // Otherwise, we should dispatch "fake" keypress event. + switch (aCommand) { + case CommandInsertLineBreak: + case CommandInsertParagraph: { + // Although, Shift+Enter and Enter are work differently in HTML + // editor, we should expose actual Shift state if it's caused by + // Enter key for compatibility with Chromium. Chromium breaks + // line in HTML editor with default pargraph separator when Enter + // is pressed, with
                    element when Shift+Enter. Safari breaks + // line in HTML editor with default paragraph separator when + // Enter, Shift+Enter or Option+Enter. So, we should not change + // Shift+Enter meaning when there was composition string or not. + NSEvent* keyEvent = + currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr; + nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent); + keypressEvent.mKeyCode = NS_VK_RETURN; + keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_Enter; + keypressEvent.mModifiers &= ~(MODIFIER_CONTROL | + MODIFIER_ALT | + MODIFIER_META); + if (aCommand == CommandInsertLineBreak) { + // In default settings, Ctrl + Enter causes insertLineBreak command. + // So, let's make Ctrl state active of the keypress event. + keypressEvent.mModifiers |= MODIFIER_CONTROL; + } + break; + } + case CommandDeleteCharBackward: + case CommandDeleteToBeginningOfLine: + case CommandDeleteWordBackward: { + NSEvent* keyEvent = + currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr; + nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent); + keypressEvent.mKeyCode = NS_VK_BACK; + keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_Backspace; + keypressEvent.mModifiers &= ~(MODIFIER_CONTROL | + MODIFIER_ALT | + MODIFIER_META); + if (aCommand == CommandDeleteToBeginningOfLine) { + keypressEvent.mModifiers |= MODIFIER_META; + } else if (aCommand == CommandDeleteWordBackward) { + keypressEvent.mModifiers |= MODIFIER_ALT; + } + break; + } + case CommandDeleteCharForward: + case CommandDeleteWordForward: { + NSEvent* keyEvent = + currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr; + nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent); + keypressEvent.mKeyCode = NS_VK_DELETE; + keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_Delete; + keypressEvent.mModifiers &= ~(MODIFIER_CONTROL | + MODIFIER_ALT | + MODIFIER_META); + if (aCommand == CommandDeleteWordForward) { + keypressEvent.mModifiers |= MODIFIER_ALT; + } + break; + } + case CommandCharNext: + case CommandSelectCharNext: + case CommandWordNext: + case CommandSelectWordNext: + case CommandEndLine: + case CommandSelectEndLine: { + NSEvent* keyEvent = + currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr; + nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent); + keypressEvent.mKeyCode = NS_VK_RIGHT; + keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_ArrowRight; + keypressEvent.mModifiers &= ~(MODIFIER_CONTROL | + MODIFIER_ALT | + MODIFIER_META); + if (aCommand == CommandSelectCharNext || + aCommand == CommandSelectWordNext || + aCommand == CommandSelectEndLine) { + keypressEvent.mModifiers |= MODIFIER_SHIFT; + } + if (aCommand == CommandWordNext || + aCommand == CommandSelectWordNext) { + keypressEvent.mModifiers |= MODIFIER_ALT; + } + if (aCommand == CommandEndLine || + aCommand == CommandSelectEndLine) { + keypressEvent.mModifiers |= MODIFIER_META; + } + break; + } + case CommandCharPrevious: + case CommandSelectCharPrevious: + case CommandWordPrevious: + case CommandSelectWordPrevious: + case CommandBeginLine: + case CommandSelectBeginLine: { + NSEvent* keyEvent = + currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr; + nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent); + keypressEvent.mKeyCode = NS_VK_LEFT; + keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_ArrowLeft; + keypressEvent.mModifiers &= ~(MODIFIER_CONTROL | + MODIFIER_ALT | + MODIFIER_META); + if (aCommand == CommandSelectCharPrevious || + aCommand == CommandSelectWordPrevious || + aCommand == CommandSelectBeginLine) { + keypressEvent.mModifiers |= MODIFIER_SHIFT; + } + if (aCommand == CommandWordPrevious || + aCommand == CommandSelectWordPrevious) { + keypressEvent.mModifiers |= MODIFIER_ALT; + } + if (aCommand == CommandBeginLine || + aCommand == CommandSelectBeginLine) { + keypressEvent.mModifiers |= MODIFIER_META; + } + break; + } + case CommandLinePrevious: + case CommandSelectLinePrevious: + case CommandMoveTop: + case CommandSelectTop: { + NSEvent* keyEvent = + currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr; + nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent); + keypressEvent.mKeyCode = NS_VK_UP; + keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_ArrowUp; + keypressEvent.mModifiers &= ~(MODIFIER_CONTROL | + MODIFIER_ALT | + MODIFIER_META); + if (aCommand == CommandSelectLinePrevious || + aCommand == CommandSelectTop) { + keypressEvent.mModifiers |= MODIFIER_SHIFT; + } + if (aCommand == CommandMoveTop || + aCommand == CommandSelectTop) { + keypressEvent.mModifiers |= MODIFIER_META; + } + break; + } + case CommandLineNext: + case CommandSelectLineNext: + case CommandMoveBottom: + case CommandSelectBottom: { + NSEvent* keyEvent = + currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr; + nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent); + keypressEvent.mKeyCode = NS_VK_DOWN; + keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_ArrowDown; + keypressEvent.mModifiers &= ~(MODIFIER_CONTROL | + MODIFIER_ALT | + MODIFIER_META); + if (aCommand == CommandSelectLineNext || + aCommand == CommandSelectBottom) { + keypressEvent.mModifiers |= MODIFIER_SHIFT; + } + if (aCommand == CommandMoveBottom || + aCommand == CommandSelectBottom) { + keypressEvent.mModifiers |= MODIFIER_META; + } + break; + } + case CommandScrollPageUp: + case CommandSelectPageUp: { + NSEvent* keyEvent = + currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr; + nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent); + keypressEvent.mKeyCode = NS_VK_PAGE_UP; + keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_PageUp; + keypressEvent.mModifiers &= ~(MODIFIER_CONTROL | + MODIFIER_ALT | + MODIFIER_META); + if (aCommand == CommandSelectPageUp) { + keypressEvent.mModifiers |= MODIFIER_SHIFT; + } + break; + } + case CommandScrollPageDown: + case CommandSelectPageDown: { + NSEvent* keyEvent = + currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr; + nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent); + keypressEvent.mKeyCode = NS_VK_PAGE_DOWN; + keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_PageDown; + keypressEvent.mModifiers &= ~(MODIFIER_CONTROL | + MODIFIER_ALT | + MODIFIER_META); + if (aCommand == CommandSelectPageDown) { + keypressEvent.mModifiers |= MODIFIER_SHIFT; + } + break; + } + case CommandScrollBottom: + case CommandScrollTop: { + NSEvent* keyEvent = + currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr; + nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent); + if (aCommand == CommandScrollBottom) { + keypressEvent.mKeyCode = NS_VK_END; + keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_End; + } else { + keypressEvent.mKeyCode = NS_VK_HOME; + keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_Home; + } + keypressEvent.mModifiers &= ~(MODIFIER_CONTROL | + MODIFIER_ALT | + MODIFIER_META); + break; + } + case CommandCancelOperation: + case CommandComplete: { + NSEvent* keyEvent = + currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr; + nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent); + keypressEvent.mKeyCode = NS_VK_ESCAPE; + keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_Escape; + keypressEvent.mModifiers &= ~(MODIFIER_CONTROL | + MODIFIER_ALT | + MODIFIER_META); + if (aCommand == CommandComplete) { + keypressEvent.mModifiers |= MODIFIER_ALT; + } + break; + } + default: + return false; + } } nsEventStatus status = nsEventStatus_eIgnore; @@ -2464,20 +2727,26 @@ TextInputHandler::InsertNewline() currentKeyEvent->mKeyPressHandled = keyPressHandled; currentKeyEvent->mKeyPressDispatched = keyPressDispatched; } - return; + return true; } // If keypress event isn't dispatched as expected, we should fallback to // using composition events. - NSAttributedString* lineBreaker = - [[NSAttributedString alloc] initWithString:@"\n"]; - InsertTextAsCommittingComposition(lineBreaker, nullptr); - if (currentKeyEvent) { - currentKeyEvent->mCompositionDispatched = true; + if (aCommand == CommandInsertLineBreak || + aCommand == CommandInsertParagraph) { + NSAttributedString* lineBreaker = + [[NSAttributedString alloc] initWithString:@"\n"]; + InsertTextAsCommittingComposition(lineBreaker, nullptr); + if (currentKeyEvent) { + currentKeyEvent->mCompositionDispatched = true; + } + [lineBreaker release]; + return true; } - [lineBreaker release]; - NS_OBJC_END_TRY_ABORT_BLOCK; + return false; + + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); } bool @@ -2545,7 +2814,26 @@ TextInputHandler::DoCommandBySelector(const char* aSelector) // Korean IME sends "insertNewline:" when committing existing composition // with Enter key press. In such case, the key operation has been consumed // by the committing composition but we still need to handle the command. - return Destroyed() || !currentKeyEvent->CanHandleCommand(); + if (Destroyed() || !currentKeyEvent->CanHandleCommand()) { + return true; + } + + // cancelOperation: command is fired after Escape or Command + Period. + // However, if ChildView implements cancelOperation:, calling + // [[ChildView super] doCommandBySelector:aSelector] when Command + Period + // causes only a call of [ChildView cancelOperation:sender]. I.e., + // [ChildView keyDown:theEvent] becomes to be never called. For avoiding + // this odd behavior, we need to handle the command before super class of + // ChildView only when current key event is proper event to fire Escape + // keypress event. + if (!strcmp(aSelector, "cancelOperatiorn:") && currentKeyEvent && + currentKeyEvent->IsProperKeyEvent(CommandCancelOperation)) { + return HandleCommand(CommandCancelOperation); + } + + // Otherwise, we've not handled the command yet. Propagate the command + // to the super class of ChildView. + return false; } diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index cb1d545ad7bb..6de61a1c8dfe 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -5587,7 +5587,299 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) - (void)insertNewline:(id)sender { if (mTextInputHandler) { - mTextInputHandler->InsertNewline(); + mTextInputHandler->HandleCommand(CommandInsertParagraph); + } +} + +- (void)insertLineBreak:(id)sender +{ + // Ctrl + Enter in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandInsertLineBreak); + } +} + +- (void) deleteBackward:(id)sender +{ + // Backspace in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandDeleteCharBackward); + } +} + +- (void) deleteBackwardByDecomposingPreviousCharacter:(id)sender +{ + // Ctrl + Backspace in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandDeleteCharBackward); + } +} + +- (void) deleteWordBackward:(id)sender +{ + // Alt + Backspace in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandDeleteWordBackward); + } +} + +- (void) deleteToBeginningOfBackward:(id)sender +{ + // Command + Backspace in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandDeleteToBeginningOfLine); + } +} + +- (void) deleteForward:(id)sender +{ + // Delete in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandDeleteCharForward); + } +} + +- (void) deleteWordForward:(id)sender +{ + // Alt + Delete in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandDeleteWordForward); + } +} + +- (void) insertTab:(id)sender +{ + // Tab in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandInsertTab); + } +} + +- (void) insertBacktab:(id)sender +{ + // Shift + Tab in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandInsertBacktab); + } +} + +- (void) moveRight:(id)sender +{ + // RightArrow in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandCharNext); + } +} + +- (void) moveRightAndModifySelection:(id)sender +{ + // Shift + RightArrow in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandSelectCharNext); + } +} + +- (void) moveWordRight:(id)sender +{ + // Alt + RightArrow in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandWordNext); + } +} + +- (void) moveWordRightAndModifySelection:(id)sender +{ + // Alt + Shift + RightArrow in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandSelectWordNext); + } +} + +- (void) moveToRightEndOfLine:(id)sender +{ + // Command + RightArrow in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandEndLine); + } +} + +- (void) moveToRightEndOfLineAndModifySelection:(id)sender +{ + // Command + Shift + RightArrow in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandSelectEndLine); + } +} + +- (void) moveLeft:(id)sender +{ + // LeftArrow in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandCharPrevious); + } +} + +- (void) moveLeftAndModifySelection:(id)sender +{ + // Shift + LeftArrow in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandSelectCharPrevious); + } +} + +- (void) moveWordLeft:(id)sender +{ + // Alt + LeftArrow in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandWordPrevious); + } +} + +- (void) moveWordLeftAndModifySelection:(id)sender +{ + // Alt + Shift + LeftArrow in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandSelectWordPrevious); + } +} + +- (void) moveToLeftEndOfLine:(id)sender +{ + // Command + LeftArrow in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandBeginLine); + } +} + +- (void) moveToLeftEndOfLineAndModifySelection:(id)sender +{ + // Command + Shift + LeftArrow in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandSelectBeginLine); + } +} + +- (void) moveUp:(id)sender +{ + // ArrowUp in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandLinePrevious); + } +} + +- (void) moveUpAndModifySelection:(id)sender +{ + // Shift + ArrowUp in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandSelectLinePrevious); + } +} + +- (void) moveToBeginningOfDocument:(id)sender +{ + // Command + ArrowUp in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandMoveTop); + } +} + +- (void) moveToBeginningOfDocumentAndModifySelection:(id)sender +{ + // Command + Shift + ArrowUp or Shift + Home in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandSelectTop); + } +} + +- (void) moveDown:(id)sender +{ + // ArrowDown in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandLineNext); + } +} + +- (void) moveDownAndModifySelection:(id)sender +{ + // Shift + ArrowDown in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandSelectLineNext); + } +} + +- (void) moveToEndOfDocument:(id)sender +{ + // Command + ArrowDown in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandMoveBottom); + } +} + +- (void) moveToEndOfDocumentAndModifySelection:(id)sender +{ + // Command + Shift + ArrowDown or Shift + End in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandSelectBottom); + } +} + +- (void) scrollPageUp:(id)sender +{ + // PageUp in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandScrollPageUp); + } +} + +- (void) pageUpAndModifySelection:(id)sender +{ + // Shift + PageUp in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandSelectPageUp); + } +} + +- (void) scrollPageDown:(id)sender +{ + // PageDown in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandScrollPageDown); + } +} + +- (void) pageDownAndModifySelection:(id)sender +{ + // Shift + PageDown in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandSelectPageDown); + } +} + +- (void) scrollToEndOfDocument:(id)sender +{ + // End in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandScrollBottom); + } +} + +- (void) scrollToBeginningOfDocument:(id)sender +{ + // Home in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandScrollTop); + } +} + +// XXX Don't decleare nor implement calcelOperation: because it +// causes not calling keyDown: for Command + Period. +// We need to handle it from doCommandBySelector:. + +- (void) complete:(id)sender +{ + // Alt + Escape or Alt + Shift + Escape in the default settings. + if (mTextInputHandler) { + mTextInputHandler->HandleCommand(CommandComplete); } } diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 861e966935f1..4909267de56a 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -6940,6 +6940,19 @@ nsWindow::GetCSDSupportLevel() { if (sCSDSupportLevel != CSD_SUPPORT_UNKNOWN) { return sCSDSupportLevel; } + + const char* decorationOverride = getenv("MOZ_GTK_TITLEBAR_DECORATION"); + if (decorationOverride) { + if (strcmp(decorationOverride, "none") == 0) { + sCSDSupportLevel = CSD_SUPPORT_NONE; + } else if (strcmp(decorationOverride, "client") == 0) { + sCSDSupportLevel = CSD_SUPPORT_FLAT; + } else if (strcmp(decorationOverride, "system") == 0) { + sCSDSupportLevel = CSD_SUPPORT_FULL; + } + return sCSDSupportLevel; + } + const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP"); if (currentDesktop) { if (strstr(currentDesktop, "GNOME") != nullptr) { diff --git a/widget/nsDeviceContextSpecProxy.cpp b/widget/nsDeviceContextSpecProxy.cpp index dcc569bf517c..8312dae325b9 100644 --- a/widget/nsDeviceContextSpecProxy.cpp +++ b/widget/nsDeviceContextSpecProxy.cpp @@ -19,6 +19,7 @@ #include "nsIPrintSession.h" #include "nsIPrintSettings.h" #include "nsIUUIDGenerator.h" +#include "private/pprio.h" using mozilla::Unused; @@ -65,17 +66,6 @@ nsDeviceContextSpecProxy::Init(nsIWidget* aWidget, return NS_ERROR_FAILURE; } - rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR, - getter_AddRefs(mRecordingDir)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mUuidGenerator = do_GetService("@mozilla.org/uuid-generator;1", &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - return NS_OK; } @@ -143,48 +133,12 @@ nsDeviceContextSpecProxy::GetPrintingScale() return mRealDeviceContextSpec->GetPrintingScale(); } -nsresult -nsDeviceContextSpecProxy::CreateUniqueTempPath(nsACString& aFilePath) -{ - MOZ_ASSERT(mRecordingDir); - MOZ_ASSERT(mUuidGenerator); - - nsID uuid; - nsresult rv = mUuidGenerator->GenerateUUIDInPlace(&uuid); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - char uuidChars[NSID_LENGTH]; - uuid.ToProvidedString(uuidChars); - mRecordingFileName.AssignASCII(uuidChars); - - nsCOMPtr recordingFile; - rv = mRecordingDir->Clone(getter_AddRefs(recordingFile)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = recordingFile->AppendNative(mRecordingFileName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return recordingFile->GetNativePath(aFilePath); -} - NS_IMETHODIMP nsDeviceContextSpecProxy::BeginDocument(const nsAString& aTitle, const nsAString& aPrintToFileName, int32_t aStartPage, int32_t aEndPage) { - nsAutoCString recordingPath; - nsresult rv = CreateUniqueTempPath(recordingPath); - if (NS_FAILED(rv)) { - return rv; - } - - mRecorder = new mozilla::layout::DrawEventRecorderPRFileDesc(recordingPath.get()); + mRecorder = new mozilla::layout::DrawEventRecorderPRFileDesc(); return mRemotePrintJob->InitializePrint(nsString(aTitle), nsString(aPrintToFileName), aStartPage, aEndPage); @@ -207,16 +161,7 @@ nsDeviceContextSpecProxy::AbortDocument() NS_IMETHODIMP nsDeviceContextSpecProxy::BeginPage() { - // Reopen the file, if necessary, ready for the next page. - if (!mRecorder->IsOpen()) { - nsAutoCString recordingPath; - nsresult rv = CreateUniqueTempPath(recordingPath); - if (NS_FAILED(rv)) { - return rv; - } - - mRecorder->OpenNew(recordingPath.get()); - } + mRecorder->OpenFD(mRemotePrintJob->GetNextPageFD()); return NS_OK; } @@ -226,7 +171,7 @@ nsDeviceContextSpecProxy::EndPage() { // Send the page recording to the parent. mRecorder->Close(); - mRemotePrintJob->ProcessPage(mRecordingFileName); + mRemotePrintJob->ProcessPage(); return NS_OK; } diff --git a/widget/nsDeviceContextSpecProxy.h b/widget/nsDeviceContextSpecProxy.h index d0ef3f9d3da9..4b1ba674c4fa 100644 --- a/widget/nsDeviceContextSpecProxy.h +++ b/widget/nsDeviceContextSpecProxy.h @@ -54,16 +54,11 @@ public: private: ~nsDeviceContextSpecProxy() {} - nsresult CreateUniqueTempPath(nsACString& aFilePath); - nsCOMPtr mPrintSettings; nsCOMPtr mPrintSession; nsCOMPtr mRealDeviceContextSpec; RefPtr mRemotePrintJob; RefPtr mRecorder; - nsCOMPtr mRecordingDir; - nsCOMPtr mUuidGenerator; - nsCString mRecordingFileName; }; #endif // nsDeviceContextSpecProxy_h