forked from mirrors/gecko-dev
		
	Merge mozilla-central to mozilla-inbound r=merge a=merge on a CLOSED TREE
This commit is contained in:
		
						commit
						e1c8aba28f
					
				
					 89 changed files with 1248 additions and 980 deletions
				
			
		|  | @ -961,10 +961,6 @@ | |||
|       <prefs/> | ||||
|       <versionRange minVersion="0" maxVersion="*" severity="3"/> | ||||
|     </emItem> | ||||
|     <emItem blockID="i446" id="{E90FA778-C2B7-41D0-9FA9-3FEC1CA54D66}"> | ||||
|       <prefs/> | ||||
|       <versionRange minVersion="0" maxVersion="*" severity="1"/> | ||||
|     </emItem> | ||||
|     <emItem blockID="i13" id="{E8E88AB0-7182-11DF-904E-6045E0D72085}"> | ||||
|       <prefs/> | ||||
|       <versionRange minVersion="0" maxVersion="*" severity="3"/> | ||||
|  | @ -1528,6 +1524,10 @@ | |||
|       <prefs/> | ||||
|       <versionRange minVersion="0" maxVersion="*" severity="1"/> | ||||
|     </emItem> | ||||
|     <emItem blockID="f7569261-f575-4719-8202-552b20d013b0" id="{7e907a15-0a4c-4ff4-b64f-5eeb8f841349}"> | ||||
|       <prefs/> | ||||
|       <versionRange minVersion="0" maxVersion="*" severity="3"/> | ||||
|     </emItem> | ||||
|     <emItem blockID="i8" id="{B13721C7-F507-4982-B2E5-502A71474FED}"> | ||||
|       <prefs/> | ||||
|       <versionRange minVersion="0" maxVersion="*" severity="1"/> | ||||
|  | @ -2139,6 +2139,10 @@ | |||
|         </targetApplication> | ||||
|       </versionRange> | ||||
|     </emItem> | ||||
|     <emItem blockID="i446" id="{E90FA778-C2B7-41D0-9FA9-3FEC1CA54D66}"> | ||||
|       <prefs/> | ||||
|       <versionRange minVersion="0" maxVersion="*" severity="1"/> | ||||
|     </emItem> | ||||
|   </emItems> | ||||
|   <pluginItems> | ||||
|     <pluginItem blockID="p416"> | ||||
|  |  | |||
|  | @ -19,7 +19,6 @@ skip-if = !e10s | |||
| [browser_urlbar_keyed_search_reflows.js] | ||||
| skip-if = (os == 'linux') || (os == 'win' && debug) # Disabled on Linux and Windows debug due to perma failures. Bug 1392320. | ||||
| [browser_urlbar_search_reflows.js] | ||||
| skip-if = (os == 'linux') # Disabled on Linux and OS X opt due to frequent failures. Bug 1385932 | ||||
| [browser_windowclose_reflows.js] | ||||
| [browser_windowopen_reflows.js] | ||||
| skip-if = os == 'linux' # Disabled due to frequent failures. Bug 1380465. | ||||
|  |  | |||
|  | @ -45,6 +45,7 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [ | |||
|       "adjustHeight@chrome://global/content/bindings/autocomplete.xml", | ||||
|       "_invalidate/this._adjustHeightTimeout<@chrome://global/content/bindings/autocomplete.xml", | ||||
|     ], | ||||
|     minTimes: 39, // This number should only ever go down - never up.
 | ||||
|     times: 51, // This number should only ever go down - never up.
 | ||||
|   }, | ||||
| 
 | ||||
|  |  | |||
|  | @ -57,7 +57,7 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [ | |||
|       "_invalidate@chrome://global/content/bindings/autocomplete.xml", | ||||
|       "invalidate@chrome://global/content/bindings/autocomplete.xml" | ||||
|     ], | ||||
|     times: 60, // This number should only ever go down - never up.
 | ||||
|     times: 36, // This number should only ever go down - never up.
 | ||||
|   }, | ||||
| 
 | ||||
|   { | ||||
|  | @ -102,6 +102,18 @@ const EXPECTED_REFLOWS_SECOND_OPEN = [ | |||
|     times: 3, // This number should only ever go down - never up.
 | ||||
|   }, | ||||
| 
 | ||||
|   { | ||||
|     stack: [ | ||||
|       "_handleOverflow@chrome://global/content/bindings/autocomplete.xml", | ||||
|       "handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml", | ||||
|       "_reuseAcItem@chrome://global/content/bindings/autocomplete.xml", | ||||
|       "_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml", | ||||
|       "_invalidate@chrome://global/content/bindings/autocomplete.xml", | ||||
|       "invalidate@chrome://global/content/bindings/autocomplete.xml" | ||||
|     ], | ||||
|     times: 24, // This number should only ever go down - never up.
 | ||||
|   }, | ||||
| 
 | ||||
|   // Bug 1359989
 | ||||
|   { | ||||
|     stack: [ | ||||
|  |  | |||
|  | @ -36,6 +36,7 @@ add_task(async function history() { | |||
|   gURLBar.focus(); | ||||
|   EventUtils.synthesizeKey("VK_DOWN", {}); | ||||
|   await promisePopupShown(gURLBar.popup); | ||||
|   await waitForAutocompleteResultAt(gMaxResults - 1) | ||||
| 
 | ||||
|   assertState(-1, -1, ""); | ||||
| 
 | ||||
|  | @ -106,7 +107,7 @@ add_task(async function() { | |||
|   // trigger autofill since that would complicate the test.
 | ||||
|   let typedValue = "browser_urlbarOneOffs"; | ||||
|   await promiseAutocompleteResultPopup(typedValue, window, true); | ||||
| 
 | ||||
|   await waitForAutocompleteResultAt(gMaxResults - 1); | ||||
|   assertState(0, -1, typedValue); | ||||
| 
 | ||||
|   // Key down through each result.  The first result is already selected, which
 | ||||
|  | @ -158,7 +159,7 @@ add_task(async function() { | |||
| add_task(async function searchWith() { | ||||
|   let typedValue = "foo"; | ||||
|   await promiseAutocompleteResultPopup(typedValue); | ||||
| 
 | ||||
|   await waitForAutocompleteResultAt(0); | ||||
|   assertState(0, -1, typedValue); | ||||
| 
 | ||||
|   let item = gURLBar.popup.richlistbox.firstChild; | ||||
|  | @ -190,7 +191,7 @@ add_task(async function oneOffClick() { | |||
|   // stricter. Even if it looks like a url, we should search.
 | ||||
|   let typedValue = "foo.bar"; | ||||
|   await promiseAutocompleteResultPopup(typedValue); | ||||
| 
 | ||||
|   await waitForAutocompleteResultAt(1); | ||||
|   assertState(0, -1, typedValue); | ||||
| 
 | ||||
|   let oneOffs = gURLBar.popup.oneOffSearchButtons.getSelectableButtons(true); | ||||
|  | @ -211,7 +212,7 @@ add_task(async function oneOffReturn() { | |||
|   // stricter. Even if it looks like a url, we should search.
 | ||||
|   let typedValue = "foo.bar"; | ||||
|   await promiseAutocompleteResultPopup(typedValue, window, true); | ||||
| 
 | ||||
|   await waitForAutocompleteResultAt(1); | ||||
|   assertState(0, -1, typedValue); | ||||
| 
 | ||||
|   // Alt+Down to select the first one-off.
 | ||||
|  |  | |||
|  | @ -320,5 +320,7 @@ async function waitForAutocompleteResultAt(index) { | |||
|     () => gURLBar.popup.richlistbox.children.length > index && | ||||
|           gURLBar.popup.richlistbox.children[index].getAttribute("ac-text") == searchString, | ||||
|     `Waiting for the autocomplete result for "${searchString}" at [${index}] to appear`); | ||||
|   // Ensure the addition is complete, for proper mouse events on the entries.
 | ||||
|   await new Promise(resolve => window.requestIdleCallback(resolve, {timeout: 1000})); | ||||
|   return gURLBar.popup.richlistbox.children[index]; | ||||
| } | ||||
|  |  | |||
|  | @ -35,7 +35,8 @@ const getSender = (extension, target, sender) => { | |||
|     // page-open listener below).
 | ||||
|     tabId = sender.tabId; | ||||
|     delete sender.tabId; | ||||
|   } else if (target instanceof Ci.nsIDOMXULElement) { | ||||
|   } else if (target instanceof Ci.nsIDOMXULElement || | ||||
|              ExtensionUtils.instanceOf(target, "HTMLIFrameElement")) { | ||||
|     tabId = tabTracker.getBrowserData(target).tabId; | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -84,21 +84,35 @@ add_task(async function() { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   async function startInputSession() { | ||||
|   async function waitForAutocompleteResultAt(index) { | ||||
|     let searchString = gURLBar.controller.searchString; | ||||
|     await BrowserTestUtils.waitForCondition( | ||||
|       () => gURLBar.popup.richlistbox.children.length > index && | ||||
|             gURLBar.popup.richlistbox.children[index].getAttribute("ac-text") == searchString, | ||||
|       `Waiting for the autocomplete result for "${searchString}" at [${index}] to appear`); | ||||
|     // Ensure the addition is complete, for proper mouse events on the entries.
 | ||||
|     await new Promise(resolve => window.requestIdleCallback(resolve, {timeout: 1000})); | ||||
|     return gURLBar.popup.richlistbox.children[index]; | ||||
|   } | ||||
| 
 | ||||
|   let inputSessionSerial = 0; | ||||
|   async function startInputSession(indexToWaitFor) { | ||||
|     gURLBar.focus(); | ||||
|     gURLBar.value = keyword; | ||||
|     EventUtils.synthesizeKey(" ", {}); | ||||
|     await expectEvent("on-input-started-fired"); | ||||
|     EventUtils.synthesizeKey("t", {}); | ||||
|     await expectEvent("on-input-changed-fired", {text: "t"}); | ||||
|     // Always use a different input at every invokation, so that
 | ||||
|     // waitForAutocompleteResultAt can distinguish different cases.
 | ||||
|     let char = ((inputSessionSerial++) % 10).toString(); | ||||
|     EventUtils.synthesizeKey(char, {}); | ||||
| 
 | ||||
|     await expectEvent("on-input-changed-fired", {text: char}); | ||||
|     // Wait for the autocomplete search. Note that we cannot wait for the search
 | ||||
|     // to be complete, since the add-on doesn't communicate when it's done, so
 | ||||
|     // just check matches count.
 | ||||
|     await BrowserTestUtils.waitForCondition( | ||||
|       () => gURLBar.controller.matchCount >= 2 && | ||||
|             gURLBar.popup.richlistbox.children[1].getAttribute("ac-text") == gURLBar.controller.searchString, | ||||
|       "waiting urlbar search to complete"); | ||||
|     return "t"; | ||||
|     await waitForAutocompleteResultAt(indexToWaitFor); | ||||
| 
 | ||||
|     return char; | ||||
|   } | ||||
| 
 | ||||
|   async function testInputEvents() { | ||||
|  | @ -174,7 +188,7 @@ add_task(async function() { | |||
|       await extension.awaitMessage("default-suggestion-set"); | ||||
|     } | ||||
| 
 | ||||
|     let text = await startInputSession(); | ||||
|     let text = await startInputSession(0); | ||||
| 
 | ||||
|     let item = gURLBar.popup.richlistbox.children[0]; | ||||
| 
 | ||||
|  | @ -193,7 +207,7 @@ add_task(async function() { | |||
|   } | ||||
| 
 | ||||
|   async function testDisposition(suggestionIndex, expectedDisposition, expectedText) { | ||||
|     await startInputSession(); | ||||
|     await startInputSession(suggestionIndex); | ||||
| 
 | ||||
|     // Select the suggestion.
 | ||||
|     for (let i = 0; i < suggestionIndex; i++) { | ||||
|  | @ -229,7 +243,7 @@ add_task(async function() { | |||
|         `Expected suggestion to have displayurl: "${keyword} ${content}".`); | ||||
|     } | ||||
| 
 | ||||
|     let text = await startInputSession(); | ||||
|     let text = await startInputSession(info.suggestions.length - 1); | ||||
| 
 | ||||
|     extension.sendMessage(info.test); | ||||
|     await extension.awaitMessage("test-ready"); | ||||
|  | @ -274,12 +288,10 @@ add_task(async function() { | |||
|   // Test adding suggestions asynchronously.
 | ||||
|   await testSuggestions({ | ||||
|     test: "test-multiple-suggest-calls", | ||||
|     skipHeuristic: true, | ||||
|     suggestions, | ||||
|   }); | ||||
|   await testSuggestions({ | ||||
|     test: "test-suggestions-after-delay", | ||||
|     skipHeuristic: true, | ||||
|     suggestions, | ||||
|   }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,8 +3,10 @@ | |||
| add_task(async function testLastAccessed() { | ||||
|   let past = Date.now(); | ||||
| 
 | ||||
|   await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/?1"); | ||||
|   await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/?2"); | ||||
|   for (let url of ["https://example.com/?1", "https://example.com/?2"]) { | ||||
|     let tab = BrowserTestUtils.addTab(gBrowser, url, {skipAnimation: true}); | ||||
|     await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, url); | ||||
|   } | ||||
| 
 | ||||
|   let extension = ExtensionTestUtils.loadExtension({ | ||||
|     manifest: { | ||||
|  | @ -12,10 +14,6 @@ add_task(async function testLastAccessed() { | |||
|     }, | ||||
|     async background() { | ||||
|       browser.test.onMessage.addListener(async function(msg, past) { | ||||
|         if (msg !== "past") { | ||||
|           return; | ||||
|         } | ||||
| 
 | ||||
|         let [tab1] = await browser.tabs.query({url: "https://example.com/?1"}); | ||||
|         let [tab2] = await browser.tabs.query({url: "https://example.com/?2"}); | ||||
| 
 | ||||
|  | @ -23,10 +21,12 @@ add_task(async function testLastAccessed() { | |||
| 
 | ||||
|         let now = Date.now(); | ||||
| 
 | ||||
|         browser.test.assertTrue(past < tab1.lastAccessed && | ||||
|                                 tab1.lastAccessed < tab2.lastAccessed && | ||||
|                                 tab2.lastAccessed <= now, | ||||
|                                 "lastAccessed timestamps are recent and in the right order"); | ||||
|         browser.test.assertTrue(past < tab1.lastAccessed, | ||||
|                                 "lastAccessed of tab 1 is later than the test start time."); | ||||
|         browser.test.assertTrue(tab1.lastAccessed < tab2.lastAccessed, | ||||
|                                 "lastAccessed of tab 2 is later than lastAccessed of tab 1."); | ||||
|         browser.test.assertTrue(tab2.lastAccessed <= now, | ||||
|                                 "lastAccessed of tab 2 is earlier than now."); | ||||
| 
 | ||||
|         await browser.tabs.remove([tab1.id, tab2.id]); | ||||
| 
 | ||||
|  |  | |||
|  | @ -321,7 +321,13 @@ charts.totalSize=Size: %S KB | |||
| # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals | ||||
| # This is the label displayed in the performance analysis view for the | ||||
| # total requests time, in seconds. | ||||
| charts.totalSeconds=Time: #1 second;Time: #1 seconds | ||||
| charts.totalSeconds=Time: %1$S second;Time: %1$S seconds | ||||
| 
 | ||||
| # LOCALIZATION NOTE (charts.totalSecondsNonBlocking): Semi-colon list of plural forms. | ||||
| # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals | ||||
| # This is the label displayed in the performance analysis view for the | ||||
| # total requests time (non-blocking), in seconds. | ||||
| charts.totalSecondsNonBlocking=Non blocking time: %1$S second;Non blocking time: %1$S seconds | ||||
| 
 | ||||
| # LOCALIZATION NOTE (charts.totalCached): This is the label displayed | ||||
| # in the performance analysis view for total cached responses. | ||||
|  | @ -348,6 +354,11 @@ charts.transferred=Transferred | |||
| # in the header column in the performance analysis view for time of request. | ||||
| charts.time=Time | ||||
| 
 | ||||
| # LOCALIZATION NOTE (charts.nonBlockingTime): This is the label displayed | ||||
| # in the header column in the performance analysis view for non blocking | ||||
| # time of request. | ||||
| charts.nonBlockingTime=Non blocking time | ||||
| 
 | ||||
| # LOCALIZATION NOTE (netRequest.headers): A label used for Headers tab | ||||
| # This tab displays list of HTTP headers | ||||
| netRequest.headers=Headers | ||||
|  |  | |||
|  | @ -132,6 +132,7 @@ class StatisticsPanel extends Component { | |||
|         size: L10N.getStr("charts.size"), | ||||
|         transferredSize: L10N.getStr("charts.transferred"), | ||||
|         time: L10N.getStr("charts.time"), | ||||
|         nonBlockingTime: L10N.getStr("charts.nonBlockingTime"), | ||||
|       }, | ||||
|       data, | ||||
|       strings: { | ||||
|  | @ -142,6 +143,8 @@ class StatisticsPanel extends Component { | |||
|             getSizeWithDecimals(value / 1024)), | ||||
|         time: (value) => | ||||
|           L10N.getFormatStr("charts.totalS", getTimeWithDecimals(value / 1000)), | ||||
|         nonBlockingTime: (value) => | ||||
|           L10N.getFormatStr("charts.totalS", getTimeWithDecimals(value / 1000)), | ||||
|       }, | ||||
|       totals: { | ||||
|         cached: (total) => L10N.getFormatStr("charts.totalCached", total), | ||||
|  | @ -155,7 +158,13 @@ class StatisticsPanel extends Component { | |||
|           let seconds = total / 1000; | ||||
|           let string = getTimeWithDecimals(seconds); | ||||
|           return PluralForm.get(seconds, | ||||
|             L10N.getStr("charts.totalSeconds")).replace("#1", string); | ||||
|             L10N.getFormatStr("charts.totalSeconds", string)); | ||||
|         }, | ||||
|         nonBlockingTime: (total) => { | ||||
|           let seconds = total / 1000; | ||||
|           let string = getTimeWithDecimals(seconds); | ||||
|           return PluralForm.get(seconds, | ||||
|             L10N.getFormatStr("charts.totalSecondsNonBlocking", string)); | ||||
|         }, | ||||
|       }, | ||||
|       sorted: true, | ||||
|  | @ -185,6 +194,7 @@ class StatisticsPanel extends Component { | |||
|       size: 0, | ||||
|       transferredSize: 0, | ||||
|       time: 0, | ||||
|       nonBlockingTime: 0, | ||||
|     })); | ||||
| 
 | ||||
|     for (let request of requests) { | ||||
|  | @ -224,6 +234,9 @@ class StatisticsPanel extends Component { | |||
|         data[type].time += request.totalTime || 0; | ||||
|         data[type].size += request.contentSize || 0; | ||||
|         data[type].transferredSize += request.transferredSize || 0; | ||||
|         let nonBlockingTime = | ||||
|            request.eventTimings.totalTime - request.eventTimings.timings.blocked; | ||||
|         data[type].nonBlockingTime += nonBlockingTime || 0; | ||||
|       } else { | ||||
|         data[type].cached++; | ||||
|       } | ||||
|  |  | |||
|  | @ -22,9 +22,6 @@ function debug(msg) { | |||
|  * this is used in RDM.  The steps described there are copied into the code | ||||
|  * below. | ||||
|  * | ||||
|  * For additional low level details about swapping browser content, | ||||
|  * see /devtools/client/responsive.html/docs/browser-swap.md. | ||||
|  * | ||||
|  * @param tab | ||||
|  *        A browser tab with content to be swapped. | ||||
|  * @param containerURL | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| const { Ci } = require("chrome"); | ||||
| const { Ci, Cu } = require("chrome"); | ||||
| const Services = require("Services"); | ||||
| const { Task } = require("devtools/shared/task"); | ||||
| const { BrowserElementWebNavigation } = require("./web-navigation"); | ||||
|  | @ -20,8 +20,7 @@ function debug(msg) { | |||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Properties swapped between browsers by browser.xml's `swapDocShells`.  See also the | ||||
|  * list at /devtools/client/responsive.html/docs/browser-swap.md. | ||||
|  * Properties swapped between browsers by browser.xml's `swapDocShells`. | ||||
|  */ | ||||
| const SWAPPED_BROWSER_STATE = [ | ||||
|   "_remoteFinder", | ||||
|  | @ -328,8 +327,8 @@ function MessageManagerTunnel(outer, inner) { | |||
|   if (outer.isRemoteBrowser) { | ||||
|     throw new Error("The outer browser must be non-remote."); | ||||
|   } | ||||
|   this.outer = outer; | ||||
|   this.inner = inner; | ||||
|   this.outerRef = Cu.getWeakReference(outer); | ||||
|   this.innerRef = Cu.getWeakReference(inner); | ||||
|   this.tunneledMessageNames = new Set(); | ||||
|   this.init(); | ||||
| } | ||||
|  | @ -415,6 +414,8 @@ MessageManagerTunnel.prototype = { | |||
|     "Finder:", | ||||
|     // Messages sent from InlineSpellChecker.jsm
 | ||||
|     "InlineSpellChecker:", | ||||
|     // Messages sent from MessageChannel.jsm
 | ||||
|     "MessageChannel:", | ||||
|     // Messages sent from pageinfo.js
 | ||||
|     "PageInfo:", | ||||
|     // Messages sent from printUtils.js
 | ||||
|  | @ -433,6 +434,8 @@ MessageManagerTunnel.prototype = { | |||
|     "Findbar:", | ||||
|     // Messages sent to RemoteFinder.jsm
 | ||||
|     "Finder:", | ||||
|     // Messages sent to MessageChannel.jsm
 | ||||
|     "MessageChannel:", | ||||
|     // Messages sent to pageinfo.js
 | ||||
|     "PageInfo:", | ||||
|     // Messages sent to printUtils.js
 | ||||
|  | @ -447,6 +450,10 @@ MessageManagerTunnel.prototype = { | |||
|     "resource://devtools/server/child.js" | ||||
|   ], | ||||
| 
 | ||||
|   get outer() { | ||||
|     return this.outerRef.get(); | ||||
|   }, | ||||
| 
 | ||||
|   get outerParentMM() { | ||||
|     if (!this.outer[FRAME_LOADER]) { | ||||
|       return null; | ||||
|  | @ -463,6 +470,10 @@ MessageManagerTunnel.prototype = { | |||
|                    .getInterface(Ci.nsIContentFrameMessageManager); | ||||
|   }, | ||||
| 
 | ||||
|   get inner() { | ||||
|     return this.innerRef.get(); | ||||
|   }, | ||||
| 
 | ||||
|   get innerParentMM() { | ||||
|     if (!this.inner.frameLoader) { | ||||
|       return null; | ||||
|  | @ -620,4 +631,8 @@ MessageManagerTunnel.prototype = { | |||
|            this.INNER_TO_OUTER_MESSAGE_PREFIXES.some(prefix => name.startsWith(prefix)); | ||||
|   }, | ||||
| 
 | ||||
|   toString() { | ||||
|     return "[object MessageManagerTunnel]"; | ||||
|   }, | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -1,144 +0,0 @@ | |||
| # Overview | ||||
| 
 | ||||
| The RDM tool uses several forms of tab and browser swapping to integrate the | ||||
| tool UI cleanly into the browser UI.  The high level steps of this process are | ||||
| documented at `/devtools/docs/responsive-design-mode.md`. | ||||
| 
 | ||||
| This document contains a random assortment of low level notes about the steps | ||||
| the browser goes through when swapping browsers between tabs. | ||||
| 
 | ||||
| # Connections between Browsers and Tabs | ||||
| 
 | ||||
| Link between tab and browser (`gBrowser._linkBrowserToTab`): | ||||
| 
 | ||||
| ``` | ||||
| aTab.linkedBrowser = browser; | ||||
| gBrowser._tabForBrowser.set(browser, aTab); | ||||
| ``` | ||||
| 
 | ||||
| # Swapping Browsers between Tabs | ||||
| 
 | ||||
| ## Legend | ||||
| 
 | ||||
| * (R): remote browsers only | ||||
| * (!R): non-remote browsers only | ||||
| 
 | ||||
| ## Functions Called | ||||
| 
 | ||||
| When you call `gBrowser.swapBrowsersAndCloseOther` to move tab content from a | ||||
| browser in one tab to a browser in another tab, here are all the code paths | ||||
| involved: | ||||
| 
 | ||||
| * `gBrowser.swapBrowsersAndCloseOther` | ||||
|   * `gBrowser._beginRemoveTab` | ||||
|     * `gBrowser.tabContainer.updateVisibility` | ||||
|     * Emit `TabClose` | ||||
|     * `browser.webProgress.removeProgressListener` | ||||
|     * `filter.removeProgressListener` | ||||
|     * `listener.destroy` | ||||
|   * `gBrowser._swapBrowserDocShells` | ||||
|     * `ourBrowser.webProgress.removeProgressListener` | ||||
|     * `filter.removeProgressListener` | ||||
|     * `gBrowser._swapRegisteredOpenURIs` | ||||
|     * `ourBrowser.swapDocShells(aOtherBrowser)` | ||||
|       * Emit `SwapDocShells` | ||||
|       * `PopupNotifications._swapBrowserNotifications` | ||||
|       * `browser.detachFormFill` (!R) | ||||
|       * `browser.swapFrameLoaders` | ||||
|       * `browser.attachFormFill` (!R) | ||||
|       * `browser._remoteWebNavigationImpl.swapBrowser(browser)` (R) | ||||
|       * `browser._remoteWebProgressManager.swapBrowser(browser)` (R) | ||||
|       * `browser._remoteFinder.swapBrowser(browser)` (R) | ||||
|       * Emit `EndSwapDocShells` | ||||
|     * `gBrowser.mTabProgressListener` | ||||
|     * `filter.addProgressListener` | ||||
|     * `ourBrowser.webProgress.addProgressListener` | ||||
|   * `gBrowser._endRemoveTab` | ||||
|     * `gBrowser._blurTab` | ||||
|     * `gBrowser._tabFilters.delete` | ||||
|     * `gBrowser._tabListeners.delete` | ||||
|     * `gBrowser._outerWindowIDBrowserMap.delete` | ||||
|     * `browser.destroy` | ||||
|     * `gBrowser.tabContainer.removeChild` | ||||
|     * `gBrowser.tabContainer.adjustTabstrip` | ||||
|     * `gBrowser.tabContainer._setPositionalAttributes` | ||||
|     * `browser.parentNode.removeChild(browser)` | ||||
|     * `gBrowser._tabForBrowser.delete` | ||||
|     * `gBrowser.mPanelContainer.removeChild` | ||||
|   * `gBrowser.setTabTitle` / `gBrowser.setTabTitleLoading` | ||||
|     * `browser.currentURI.spec` | ||||
|     * `gBrowser._tabAttrModified` | ||||
|     * `gBrowser.updateTitlebar` | ||||
|   * `gBrowser.updateCurrentBrowser` | ||||
|     * `browser.docShellIsActive` (!R) | ||||
|     * `gBrowser.showTab` | ||||
|     * `gBrowser._appendStatusPanel` | ||||
|     * `gBrowser._callProgressListeners` with `onLocationChange` | ||||
|     * `gBrowser._callProgressListeners` with `onSecurityChange` | ||||
|     * `gBrowser._callProgressListeners` with `onUpdateCurrentBrowser` | ||||
|     * `gBrowser.updateTitlebar` | ||||
|     * `gBrowser._callProgressListeners` with `onStateChange` | ||||
|     * `gBrowser._setCloseKeyState` | ||||
|     * Emit `TabSelect` | ||||
|     * `gBrowser._tabAttrModified` | ||||
|     * `browser.getInPermitUnload` | ||||
|     * `gBrowser.tabContainer._setPositionalAttributes` | ||||
|   * `gBrowser._tabAttrModified` | ||||
| 
 | ||||
| ## Browser State | ||||
| 
 | ||||
| When calling `gBrowser.swapBrowsersAndCloseOther`, the browser is not actually | ||||
| moved from one tab to the other.  Instead, various properties _on_ each of the | ||||
| browsers are swapped. | ||||
| 
 | ||||
| Browser attributes `gBrowser.swapBrowsersAndCloseOther` transfers between | ||||
| browsers: | ||||
| 
 | ||||
| * `usercontextid` | ||||
| 
 | ||||
| Tab attributes `gBrowser.swapBrowsersAndCloseOther` transfers between tabs: | ||||
| 
 | ||||
| * `usercontextid` | ||||
| * `muted` | ||||
| * `soundplaying` | ||||
| * `busy` | ||||
| 
 | ||||
| Browser properties `gBrowser.swapBrowsersAndCloseOther` transfers between | ||||
| browsers: | ||||
| 
 | ||||
| * `mIconURL` | ||||
| * `getFindBar(aOurTab)._findField.value` | ||||
| 
 | ||||
| Browser properties `gBrowser._swapBrowserDocShells` transfers between browsers: | ||||
| 
 | ||||
| * `outerWindowID` in `gBrowser._outerWindowIDBrowserMap` | ||||
| * `_outerWindowID` on the browser (R) | ||||
| * `docShellIsActive` | ||||
| * `permanentKey` | ||||
| * `registeredOpenURI` | ||||
| 
 | ||||
| Browser properties `browser.swapDocShells` transfers between browsers: | ||||
| 
 | ||||
| * `_docShell` | ||||
| * `_webBrowserFind` | ||||
| * `_contentWindow` | ||||
| * `_webNavigation` | ||||
| * `_remoteWebNavigation` (R) | ||||
| * `_remoteWebNavigationImpl` (R) | ||||
| * `_remoteWebProgressManager` (R) | ||||
| * `_remoteWebProgress` (R) | ||||
| * `_remoteFinder` (R) | ||||
| * `_securityUI` (R) | ||||
| * `_documentURI` (R) | ||||
| * `_documentContentType` (R) | ||||
| * `_contentTitle` (R) | ||||
| * `_characterSet` (R) | ||||
| * `_contentPrincipal` (R) | ||||
| * `_imageDocument` (R) | ||||
| * `_fullZoom` (R) | ||||
| * `_textZoom` (R) | ||||
| * `_isSyntheticDocument` (R) | ||||
| * `_innerWindowID` (R) | ||||
| * `_manifestURI` (R) | ||||
| 
 | ||||
| `browser.swapFrameLoaders` swaps the actual page content. | ||||
|  | @ -27,6 +27,8 @@ support-files = | |||
| [browser_device_width.js] | ||||
| [browser_dpr_change.js] | ||||
| [browser_exit_button.js] | ||||
| [browser_ext_messaging.js] | ||||
| tags = devtools webextensions | ||||
| [browser_frame_script_active.js] | ||||
| [browser_hide_container.js] | ||||
| [browser_menu_item_01.js] | ||||
|  | @ -37,7 +39,7 @@ skip-if = true # Bug 1413765 | |||
| [browser_network_throttling.js] | ||||
| [browser_page_state.js] | ||||
| [browser_permission_doorhanger.js] | ||||
| tags = geolocation | ||||
| tags = devtools geolocation | ||||
| skip-if = true # Bug 1413765 | ||||
| [browser_resize_cmd.js] | ||||
| [browser_screenshot_button.js] | ||||
|  |  | |||
|  | @ -0,0 +1,129 @@ | |||
| /* Any copyright is dedicated to the Public Domain. | ||||
|    http://creativecommons.org/publicdomain/zero/1.0/ */
 | ||||
| 
 | ||||
| /* eslint-env webextensions */ | ||||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| const TEST_URL = "http://example.com/"; | ||||
| 
 | ||||
| // These allowed rejections are copied from
 | ||||
| // browser/components/extensions/test/browser/head.js.
 | ||||
| PromiseTestUtils.whitelistRejectionsGlobally(/Message manager disconnected/); | ||||
| PromiseTestUtils.whitelistRejectionsGlobally(/Receiving end does not exist/); | ||||
| 
 | ||||
| add_task(async function () { | ||||
|   let tab = await addTab(TEST_URL); | ||||
|   await openRDM(tab); | ||||
| 
 | ||||
|   let extension = ExtensionTestUtils.loadExtension({ | ||||
|     manifest: { | ||||
|       "permissions": ["tabs"], | ||||
| 
 | ||||
|       "content_scripts": [{ | ||||
|         "matches": [TEST_URL], | ||||
|         "js": ["content-script.js"], | ||||
|         "run_at": "document_start", | ||||
|       }], | ||||
|     }, | ||||
| 
 | ||||
|     async background() { | ||||
|       const TEST_URL = "http://example.com/"; // eslint-disable-line no-shadow
 | ||||
| 
 | ||||
|       browser.test.log("Background script init"); | ||||
| 
 | ||||
|       let extTab; | ||||
|       let contentMessage = new Promise(resolve => { | ||||
|         browser.test.log("Listen to content"); | ||||
|         let listener = async (msg, sender, respond) => { | ||||
|           browser.test.assertEq(msg, "hello-from-content", | ||||
|             "Background script got hello-from-content message"); | ||||
| 
 | ||||
|           let tabs = await browser.tabs.query({ | ||||
|             currentWindow: true, | ||||
|             active: true, | ||||
|           }); | ||||
|           browser.test.assertEq(tabs.length, 1, | ||||
|             "One tab is active in the current window"); | ||||
|           extTab = tabs[0]; | ||||
|           browser.test.log(`Tab: id ${extTab.id}, url ${extTab.url}`); | ||||
|           browser.test.assertEq(extTab.url, TEST_URL, "Tab has the test URL"); | ||||
| 
 | ||||
|           browser.test.assertTrue(!!sender, "Message has a sender"); | ||||
|           browser.test.assertTrue(!!sender.tab, "Message has a sender.tab"); | ||||
|           browser.test.assertEq(sender.tab.id, extTab.id, | ||||
|             "Sender's tab ID matches the RDM tab ID"); | ||||
|           browser.test.assertEq(sender.tab.url, extTab.url, | ||||
|             "Sender's tab URL matches the RDM tab URL"); | ||||
| 
 | ||||
|           browser.runtime.onMessage.removeListener(listener); | ||||
|           resolve(); | ||||
|         }; | ||||
|         browser.runtime.onMessage.addListener(listener); | ||||
|       }); | ||||
| 
 | ||||
|       // Wait for "resume" message so we know the content script is also ready.
 | ||||
|       await new Promise(resolve => { | ||||
|         browser.test.onMessage.addListener(resolve); | ||||
|         browser.test.sendMessage("background-script-ready"); | ||||
|       }); | ||||
| 
 | ||||
|       await contentMessage; | ||||
| 
 | ||||
|       browser.test.log("Send message from background to content"); | ||||
|       let contentSender = await browser.tabs.sendMessage( | ||||
|         extTab.id, | ||||
|         "hello-from-background" | ||||
|       ); | ||||
|       browser.test.assertEq(contentSender.id, browser.runtime.id, | ||||
|         "The sender ID in content matches this extension"); | ||||
| 
 | ||||
|       browser.test.notifyPass("rdm-messaging"); | ||||
|     }, | ||||
| 
 | ||||
|     files: { | ||||
|       "content-script.js": async function () { | ||||
|         browser.test.log("Content script init"); | ||||
| 
 | ||||
|         browser.test.log("Listen to background"); | ||||
|         browser.runtime.onMessage.addListener((msg, sender, respond) => { | ||||
|           browser.test.assertEq(msg, "hello-from-background", | ||||
|             "Content script got hello-from-background message"); | ||||
| 
 | ||||
|           browser.test.assertTrue(!!sender, "Message has a sender"); | ||||
|           browser.test.assertTrue(!!sender.id, "Message has a sender.id"); | ||||
| 
 | ||||
|           let { id } = sender; | ||||
|           respond({ id }); | ||||
|         }); | ||||
| 
 | ||||
|         // Wait for "resume" message so we know the background script is also ready.
 | ||||
|         await new Promise(resolve => { | ||||
|           browser.test.onMessage.addListener(resolve); | ||||
|           browser.test.sendMessage("content-script-ready"); | ||||
|         }); | ||||
| 
 | ||||
|         browser.test.log("Send message from content to background"); | ||||
|         browser.runtime.sendMessage("hello-from-content"); | ||||
|       }, | ||||
|     }, | ||||
|   }); | ||||
| 
 | ||||
|   let contentScriptReady = extension.awaitMessage("content-script-ready"); | ||||
|   let backgroundScriptReady = extension.awaitMessage("background-script-ready"); | ||||
|   let finish = extension.awaitFinish("rdm-messaging"); | ||||
| 
 | ||||
|   await extension.startup(); | ||||
| 
 | ||||
|   // It appears the background script and content script can loaded in either order, so
 | ||||
|   // we'll wait for the both to listen before proceeding.
 | ||||
|   await backgroundScriptReady; | ||||
|   await contentScriptReady; | ||||
|   extension.sendMessage("resume"); | ||||
| 
 | ||||
|   await finish; | ||||
|   await extension.unload(); | ||||
| 
 | ||||
|   await closeRDM(tab); | ||||
|   await removeTab(tab); | ||||
| }); | ||||
|  | @ -13559,6 +13559,12 @@ nsDocShell::ConfirmRepost(bool* aRepost) | |||
|     return rv; | ||||
|   } | ||||
| 
 | ||||
|   // Make the repost prompt tab modal to prevent malicious pages from locking
 | ||||
|   // up the browser, see bug 1412559 for an example.
 | ||||
|   if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompter)) { | ||||
|     promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), true); | ||||
|   } | ||||
| 
 | ||||
|   int32_t buttonPressed; | ||||
|   // The actual value here is irrelevant, but we can't pass an invalid
 | ||||
|   // bool through XPConnect.
 | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ | |||
| #include "mozilla/EditorBase.h" | ||||
| #include "mozilla/dom/Selection.h" | ||||
| #include "mozilla/mozalloc.h" | ||||
| #include "mozilla/RangeBoundary.h" | ||||
| #include "nsCOMPtr.h" | ||||
| #include "nsDebug.h" | ||||
| #include "nsError.h" | ||||
|  | @ -56,46 +57,54 @@ DeleteRangeTransaction::DoTransaction() | |||
|   rangeToDelete.swap(mRangeToDelete); | ||||
| 
 | ||||
|   // build the child transactions
 | ||||
|   nsCOMPtr<nsINode> startContainer = rangeToDelete->GetStartContainer(); | ||||
|   int32_t startOffset = rangeToDelete->StartOffset(); | ||||
|   nsCOMPtr<nsINode> endContainer = rangeToDelete->GetEndContainer(); | ||||
|   int32_t endOffset = rangeToDelete->EndOffset(); | ||||
|   MOZ_ASSERT(startContainer && endContainer); | ||||
|   const RangeBoundary& startRef = rangeToDelete->StartRef(); | ||||
|   const RangeBoundary& endRef = rangeToDelete->EndRef(); | ||||
|   MOZ_ASSERT(startRef.IsSetAndValid()); | ||||
|   MOZ_ASSERT(endRef.IsSetAndValid()); | ||||
| 
 | ||||
|   if (startContainer == endContainer) { | ||||
|   if (startRef.Container() == endRef.Container()) { | ||||
|     // the selection begins and ends in the same node
 | ||||
|     nsIContent* startChild = rangeToDelete->GetChildAtStartOffset(); | ||||
|     nsresult rv = | ||||
|       CreateTxnsToDeleteBetween(startContainer, startOffset, | ||||
|                                 startChild, endOffset); | ||||
|     NS_ENSURE_SUCCESS(rv, rv); | ||||
|     nsresult rv = CreateTxnsToDeleteBetween(startRef.AsRaw(), endRef.AsRaw()); | ||||
|     if (NS_WARN_IF(NS_FAILED(rv))) { | ||||
|       return rv; | ||||
|     } | ||||
|   } else { | ||||
|     // the selection ends in a different node from where it started.  delete
 | ||||
|     // the relevant content in the start node
 | ||||
|     nsresult rv = | ||||
|       CreateTxnsToDeleteContent(startContainer, startOffset, nsIEditor::eNext); | ||||
|     NS_ENSURE_SUCCESS(rv, rv); | ||||
|     nsresult rv = CreateTxnsToDeleteContent(startRef.AsRaw(), nsIEditor::eNext); | ||||
|     if (NS_WARN_IF(NS_FAILED(rv))) { | ||||
|       return rv; | ||||
|     } | ||||
|     // delete the intervening nodes
 | ||||
|     rv = CreateTxnsToDeleteNodesBetween(rangeToDelete); | ||||
|     NS_ENSURE_SUCCESS(rv, rv); | ||||
|     if (NS_WARN_IF(NS_FAILED(rv))) { | ||||
|       return rv; | ||||
|     } | ||||
|     // delete the relevant content in the end node
 | ||||
|     rv = CreateTxnsToDeleteContent(endContainer, endOffset, | ||||
|                                    nsIEditor::ePrevious); | ||||
|     NS_ENSURE_SUCCESS(rv, rv); | ||||
|     rv = CreateTxnsToDeleteContent(endRef.AsRaw(), nsIEditor::ePrevious); | ||||
|     if (NS_WARN_IF(NS_FAILED(rv))) { | ||||
|       return rv; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // if we've successfully built this aggregate transaction, then do it.
 | ||||
|   nsresult rv = EditAggregateTransaction::DoTransaction(); | ||||
|   NS_ENSURE_SUCCESS(rv, rv); | ||||
|   if (NS_WARN_IF(NS_FAILED(rv))) { | ||||
|     return rv; | ||||
|   } | ||||
| 
 | ||||
|   // only set selection to deletion point if editor gives permission
 | ||||
|   bool bAdjustSelection; | ||||
|   mEditorBase->ShouldTxnSetSelection(&bAdjustSelection); | ||||
|   if (bAdjustSelection) { | ||||
|     RefPtr<Selection> selection = mEditorBase->GetSelection(); | ||||
|     NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); | ||||
|     rv = selection->Collapse(startContainer, startOffset); | ||||
|     NS_ENSURE_SUCCESS(rv, rv); | ||||
|     if (NS_WARN_IF(!selection)) { | ||||
|       return NS_ERROR_NULL_POINTER; | ||||
|     } | ||||
|     rv = selection->Collapse(startRef.AsRaw()); | ||||
|     if (NS_WARN_IF(NS_FAILED(rv))) { | ||||
|       return rv; | ||||
|     } | ||||
|   } | ||||
|   // else do nothing - dom range gravity will adjust selection
 | ||||
| 
 | ||||
|  | @ -122,30 +131,36 @@ DeleteRangeTransaction::GetTxnDescription(nsAString& aString) | |||
| } | ||||
| 
 | ||||
| nsresult | ||||
| DeleteRangeTransaction::CreateTxnsToDeleteBetween(nsINode* aNode, | ||||
|                                                   int32_t aStartOffset, | ||||
|                                                   nsIContent* aChildAtStartOffset, | ||||
|                                                   int32_t aEndOffset) | ||||
| DeleteRangeTransaction::CreateTxnsToDeleteBetween( | ||||
|                           const RawRangeBoundary& aStart, | ||||
|                           const RawRangeBoundary& aEnd) | ||||
| { | ||||
|   if (NS_WARN_IF(!aStart.IsSetAndValid()) || | ||||
|       NS_WARN_IF(!aEnd.IsSetAndValid()) || | ||||
|       NS_WARN_IF(aStart.Container() != aEnd.Container())) { | ||||
|     return NS_ERROR_INVALID_ARG; | ||||
|   } | ||||
| 
 | ||||
|   if (NS_WARN_IF(!mEditorBase)) { | ||||
|     return NS_ERROR_NOT_AVAILABLE; | ||||
|   } | ||||
| 
 | ||||
|   // see what kind of node we have
 | ||||
|   if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) { | ||||
|   if (aStart.Container()->IsNodeOfType(nsINode::eDATA_NODE)) { | ||||
|     // if the node is a chardata node, then delete chardata content
 | ||||
|     int32_t numToDel; | ||||
|     if (aStartOffset == aEndOffset) { | ||||
|     if (aStart == aEnd) { | ||||
|       numToDel = 1; | ||||
|     } else { | ||||
|       numToDel = aEndOffset - aStartOffset; | ||||
|       numToDel = aEnd.Offset() - aStart.Offset(); | ||||
|       MOZ_DIAGNOSTIC_ASSERT(numToDel > 0); | ||||
|     } | ||||
| 
 | ||||
|     RefPtr<nsGenericDOMDataNode> charDataNode = | ||||
|       static_cast<nsGenericDOMDataNode*>(aNode); | ||||
|       static_cast<nsGenericDOMDataNode*>(aStart.Container()); | ||||
| 
 | ||||
|     RefPtr<DeleteTextTransaction> deleteTextTransaction = | ||||
|       new DeleteTextTransaction(*mEditorBase, *charDataNode, aStartOffset, | ||||
|       new DeleteTextTransaction(*mEditorBase, *charDataNode, aStart.Offset(), | ||||
|                                 numToDel, mRangeUpdater); | ||||
|     // If the text node isn't editable, it should be never undone/redone.
 | ||||
|     // So, the transaction shouldn't be recorded.
 | ||||
|  | @ -156,13 +171,11 @@ DeleteRangeTransaction::CreateTxnsToDeleteBetween(nsINode* aNode, | |||
|     return NS_OK; | ||||
|   } | ||||
| 
 | ||||
|   nsIContent* child = aChildAtStartOffset; | ||||
|   for (int32_t i = aStartOffset; i < aEndOffset; ++i) { | ||||
|     // Even if we detect invalid range, we should ignore it for removing
 | ||||
|     // specified range's nodes as far as possible.
 | ||||
|     if (NS_WARN_IF(!child)) { | ||||
|       break; | ||||
|     } | ||||
|   // Even if we detect invalid range, we should ignore it for removing
 | ||||
|   // specified range's nodes as far as possible.
 | ||||
|   for (nsIContent* child = aStart.GetChildAtOffset(); | ||||
|        child && child != aEnd.GetChildAtOffset(); | ||||
|        child = child->GetNextSibling()) { | ||||
|     RefPtr<DeleteNodeTransaction> deleteNodeTransaction = | ||||
|       new DeleteNodeTransaction(*mEditorBase, *child, mRangeUpdater); | ||||
|     // XXX This is odd handling.  Even if some children are not editable,
 | ||||
|  | @ -172,48 +185,54 @@ DeleteRangeTransaction::CreateTxnsToDeleteBetween(nsINode* aNode, | |||
|     if (deleteNodeTransaction->CanDoIt()) { | ||||
|       AppendChild(deleteNodeTransaction); | ||||
|     } | ||||
|     child = child->GetNextSibling(); | ||||
|   } | ||||
| 
 | ||||
|   return NS_OK; | ||||
| } | ||||
| 
 | ||||
| nsresult | ||||
| DeleteRangeTransaction::CreateTxnsToDeleteContent(nsINode* aNode, | ||||
|                                                   int32_t aOffset, | ||||
|                                                   nsIEditor::EDirection aAction) | ||||
| DeleteRangeTransaction::CreateTxnsToDeleteContent( | ||||
|                           const RawRangeBoundary& aPoint, | ||||
|                           nsIEditor::EDirection aAction) | ||||
| { | ||||
|   if (NS_WARN_IF(!aPoint.IsSetAndValid())) { | ||||
|     return NS_ERROR_INVALID_ARG; | ||||
|   } | ||||
| 
 | ||||
|   if (NS_WARN_IF(!mEditorBase)) { | ||||
|     return NS_ERROR_NOT_AVAILABLE; | ||||
|   } | ||||
| 
 | ||||
|   // see what kind of node we have
 | ||||
|   if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) { | ||||
|     // if the node is a chardata node, then delete chardata content
 | ||||
|     uint32_t start, numToDelete; | ||||
|     if (nsIEditor::eNext == aAction) { | ||||
|       start = aOffset; | ||||
|       numToDelete = aNode->Length() - aOffset; | ||||
|     } else { | ||||
|       start = 0; | ||||
|       numToDelete = aOffset; | ||||
|     } | ||||
| 
 | ||||
|     if (numToDelete) { | ||||
|       RefPtr<nsGenericDOMDataNode> dataNode = | ||||
|         static_cast<nsGenericDOMDataNode*>(aNode); | ||||
|       RefPtr<DeleteTextTransaction> deleteTextTransaction = | ||||
|         new DeleteTextTransaction(*mEditorBase, *dataNode, start, numToDelete, | ||||
|                                   mRangeUpdater); | ||||
|       // If the text node isn't editable, it should be never undone/redone.
 | ||||
|       // So, the transaction shouldn't be recorded.
 | ||||
|       if (NS_WARN_IF(!deleteTextTransaction->CanDoIt())) { | ||||
|         return NS_ERROR_FAILURE; | ||||
|       } | ||||
|       AppendChild(deleteTextTransaction); | ||||
|     } | ||||
|   if (!aPoint.Container()->IsNodeOfType(nsINode::eDATA_NODE)) { | ||||
|     return NS_OK; | ||||
|   } | ||||
| 
 | ||||
|   // If the node is a chardata node, then delete chardata content
 | ||||
|   uint32_t startOffset, numToDelete; | ||||
|   if (nsIEditor::eNext == aAction) { | ||||
|     startOffset = aPoint.Offset(); | ||||
|     numToDelete = aPoint.Container()->Length() - aPoint.Offset(); | ||||
|   } else { | ||||
|     startOffset = 0; | ||||
|     numToDelete = aPoint.Offset(); | ||||
|   } | ||||
| 
 | ||||
|   if (!numToDelete) { | ||||
|     return NS_OK; | ||||
|   } | ||||
| 
 | ||||
|   RefPtr<nsGenericDOMDataNode> dataNode = | ||||
|     static_cast<nsGenericDOMDataNode*>(aPoint.Container()); | ||||
|   RefPtr<DeleteTextTransaction> deleteTextTransaction = | ||||
|     new DeleteTextTransaction(*mEditorBase, *dataNode, startOffset, numToDelete, | ||||
|                               mRangeUpdater); | ||||
|   // If the text node isn't editable, it should be never undone/redone.
 | ||||
|   // So, the transaction shouldn't be recorded.
 | ||||
|   if (NS_WARN_IF(!deleteTextTransaction->CanDoIt())) { | ||||
|     return NS_ERROR_FAILURE; | ||||
|   } | ||||
|   AppendChild(deleteTextTransaction); | ||||
| 
 | ||||
|   return NS_OK; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #define DeleteRangeTransaction_h | ||||
| 
 | ||||
| #include "EditAggregateTransaction.h" | ||||
| #include "mozilla/RangeBoundary.h" | ||||
| #include "nsCycleCollectionParticipant.h" | ||||
| #include "nsID.h" | ||||
| #include "nsIEditor.h" | ||||
|  | @ -50,15 +51,53 @@ public: | |||
|   } | ||||
| 
 | ||||
| protected: | ||||
|   nsresult CreateTxnsToDeleteBetween(nsINode* aNode, | ||||
|                                      int32_t aStartOffset, | ||||
|                                      nsIContent* aChildAtStartOffset, | ||||
|                                      int32_t aEndOffset); | ||||
|   /**
 | ||||
|    * CreateTxnsToDeleteBetween() creates a DeleteTextTransaction or some | ||||
|    * DeleteNodeTransactions to remove text or nodes between aStart and aEnd | ||||
|    * and appends the created transactions to the array. | ||||
|    * | ||||
|    * @param aStart      Must be set and valid point. | ||||
|    * @param aEnd        Must be set and valid point.  Additionally, the | ||||
|    *                    container must be same as aStart's container. | ||||
|    *                    And of course, this must not be before aStart in | ||||
|    *                    the DOM tree order. | ||||
|    * @return            Returns NS_OK in most cases. | ||||
|    *                    When the arguments are invalid, returns | ||||
|    *                    NS_ERROR_INVALID_ARG. | ||||
|    *                    When mEditorBase isn't available, returns | ||||
|    *                    NS_ERROR_NOT_AVAIALBLE. | ||||
|    *                    When created DeleteTextTransaction cannot do its | ||||
|    *                    transaction, returns NS_ERROR_FAILURE. | ||||
|    *                    Note that even if one of created DeleteNodeTransaction | ||||
|    *                    cannot do its transaction, this returns NS_OK. | ||||
|    */ | ||||
|   nsresult CreateTxnsToDeleteBetween(const RawRangeBoundary& aStart, | ||||
|                                      const RawRangeBoundary& aEnd); | ||||
| 
 | ||||
|   nsresult CreateTxnsToDeleteNodesBetween(nsRange* aRangeToDelete); | ||||
| 
 | ||||
|   nsresult CreateTxnsToDeleteContent(nsINode* aParent, | ||||
|                                      int32_t aOffset, | ||||
|   /**
 | ||||
|    * CreateTxnsToDeleteContent() creates a DeleteTextTransaction to delete | ||||
|    * text between start of aPoint.Container() and aPoint or aPoint and end of | ||||
|    * aPoint.Container() and appends the created transaction to the array. | ||||
|    * | ||||
|    * @param aPoint      Must be set and valid point.  If the container is not | ||||
|    *                    a data node, this method does nothing. | ||||
|    * @param aAction     If nsIEditor::eNext, this method creates a transaction | ||||
|    *                    to delete text from aPoint to the end of the data node. | ||||
|    *                    Otherwise, this method creates a transaction to delete | ||||
|    *                    text from start of the data node to aPoint. | ||||
|    * @return            Returns NS_OK in most cases. | ||||
|    *                    When the arguments are invalid, returns | ||||
|    *                    NS_ERROR_INVALID_ARG. | ||||
|    *                    When mEditorBase isn't available, returns | ||||
|    *                    NS_ERROR_NOT_AVAIALBLE. | ||||
|    *                    When created DeleteTextTransaction cannot do its | ||||
|    *                    transaction, returns NS_ERROR_FAILURE. | ||||
|    *                    Note that even if no character will be deleted, | ||||
|    *                    this returns NS_OK. | ||||
|    */ | ||||
|   nsresult CreateTxnsToDeleteContent(const RawRangeBoundary& aPoint, | ||||
|                                      nsIEditor::EDirection aAction); | ||||
| 
 | ||||
|   // The editor for this transaction.
 | ||||
|  |  | |||
|  | @ -404,8 +404,8 @@ public: | |||
|   { | ||||
|     MOZ_ASSERT(IsValid()); | ||||
| 
 | ||||
|     if (mLayer->AsContainerLayer()) { | ||||
|       return mLayer->AsContainerLayer()->GetEventRegionsOverride(); | ||||
|     if (mLayer->AsRefLayer()) { | ||||
|       return mLayer->AsRefLayer()->GetEventRegionsOverride(); | ||||
|     } | ||||
|     return EventRegionsOverride::NoOverride; | ||||
|   } | ||||
|  |  | |||
|  | @ -840,8 +840,7 @@ ContainerLayer::ContainerLayer(LayerManager* aManager, void* aImplData) | |||
|     mUseIntermediateSurface(false), | ||||
|     mSupportsComponentAlphaChildren(false), | ||||
|     mMayHaveReadbackChild(false), | ||||
|     mChildrenChanged(false), | ||||
|     mEventRegionsOverride(EventRegionsOverride::NoOverride) | ||||
|     mChildrenChanged(false) | ||||
| { | ||||
| } | ||||
| 
 | ||||
|  | @ -1035,8 +1034,7 @@ ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs) | |||
| { | ||||
|   aAttrs = ContainerLayerAttributes(mPreXScale, mPreYScale, | ||||
|                                     mInheritedXScale, mInheritedYScale, | ||||
|                                     mPresShellResolution, mScaleToResolution, | ||||
|                                     mEventRegionsOverride); | ||||
|                                     mPresShellResolution, mScaleToResolution); | ||||
| } | ||||
| 
 | ||||
| bool | ||||
|  | @ -2062,12 +2060,6 @@ ContainerLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix) | |||
|   if (mScaleToResolution) { | ||||
|     aStream << nsPrintfCString(" [presShellResolution=%g]", mPresShellResolution).get(); | ||||
|   } | ||||
|   if (mEventRegionsOverride & EventRegionsOverride::ForceDispatchToContent) { | ||||
|     aStream << " [force-dtc]"; | ||||
|   } | ||||
|   if (mEventRegionsOverride & EventRegionsOverride::ForceEmptyHitRegion) { | ||||
|     aStream << " [force-ehr]"; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void | ||||
|  | @ -2244,6 +2236,12 @@ RefLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix) | |||
|   if (0 != mId) { | ||||
|     AppendToString(aStream, mId, " [id=", "]"); | ||||
|   } | ||||
|   if (mEventRegionsOverride & EventRegionsOverride::ForceDispatchToContent) { | ||||
|     aStream << " [force-dtc]"; | ||||
|   } | ||||
|   if (mEventRegionsOverride & EventRegionsOverride::ForceEmptyHitRegion) { | ||||
|     aStream << " [force-ehr]"; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void | ||||
|  |  | |||
|  | @ -2315,20 +2315,6 @@ public: | |||
|     mChildrenChanged = aVal; | ||||
|   } | ||||
| 
 | ||||
|   void SetEventRegionsOverride(EventRegionsOverride aVal) { | ||||
|     if (mEventRegionsOverride == aVal) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) EventRegionsOverride", this)); | ||||
|     mEventRegionsOverride = aVal; | ||||
|     Mutated(); | ||||
|   } | ||||
| 
 | ||||
|   EventRegionsOverride GetEventRegionsOverride() const { | ||||
|     return mEventRegionsOverride; | ||||
|   } | ||||
| 
 | ||||
|   // If |aRect| is null, the entire layer should be considered invalid for
 | ||||
|   // compositing.
 | ||||
|   virtual void SetInvalidCompositeRect(const gfx::IntRect* aRect) {} | ||||
|  | @ -2418,7 +2404,6 @@ protected: | |||
|   // This is updated by ComputeDifferences. This will be true if we need to invalidate
 | ||||
|   // the intermediate surface.
 | ||||
|   bool mChildrenChanged; | ||||
|   EventRegionsOverride mEventRegionsOverride; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -2837,6 +2822,25 @@ public: | |||
|     aLayer->SetParent(this); | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * CONSTRUCTION PHASE ONLY | ||||
|    * Set flags that indicate how event regions in the child layer tree need | ||||
|    * to be overridden because of properties of the parent layer tree. | ||||
|    */ | ||||
|   void SetEventRegionsOverride(EventRegionsOverride aVal) { | ||||
|     if (mEventRegionsOverride == aVal) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) EventRegionsOverride", this)); | ||||
|     mEventRegionsOverride = aVal; | ||||
|     Mutated(); | ||||
|   } | ||||
| 
 | ||||
|   EventRegionsOverride GetEventRegionsOverride() const { | ||||
|     return mEventRegionsOverride; | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * DRAWING PHASE ONLY | ||||
|    * |aLayer| is the same as the argument to ConnectReferentLayer(). | ||||
|  | @ -2861,7 +2865,9 @@ public: | |||
| 
 | ||||
| protected: | ||||
|   RefLayer(LayerManager* aManager, void* aImplData) | ||||
|     : ContainerLayer(aManager, aImplData) , mId(0) | ||||
|     : ContainerLayer(aManager, aImplData) | ||||
|     , mId(0) | ||||
|     , mEventRegionsOverride(EventRegionsOverride::NoOverride) | ||||
|   {} | ||||
| 
 | ||||
|   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override; | ||||
|  | @ -2870,6 +2876,7 @@ protected: | |||
| 
 | ||||
|   // 0 is a special value that means "no ID".
 | ||||
|   uint64_t mId; | ||||
|   EventRegionsOverride mEventRegionsOverride; | ||||
| }; | ||||
| 
 | ||||
| void SetAntialiasingFlags(Layer* aLayer, gfx::DrawTarget* aTarget); | ||||
|  |  | |||
|  | @ -203,7 +203,7 @@ struct EventRegions { | |||
|   } | ||||
| }; | ||||
| 
 | ||||
| // Bit flags that go on a ContainerLayer (or RefLayer) and override the
 | ||||
| // Bit flags that go on a RefLayer and override the
 | ||||
| // event regions in the entire subtree below. This is needed for propagating
 | ||||
| // various flags across processes since the child-process layout code doesn't
 | ||||
| // know about parent-process listeners or CSS rules.
 | ||||
|  |  | |||
|  | @ -651,6 +651,10 @@ GetEventRegionsOverride(HitTestingTreeNode* aParent, | |||
|   // layer in the hit-test tree. This saves having to walk up the tree every
 | ||||
|   // we want to see if a hit-test node is affected by this flag.
 | ||||
|   EventRegionsOverride result = aLayer.GetEventRegionsOverride(); | ||||
|   if (result != EventRegionsOverride::NoOverride) { | ||||
|     // Overrides should only ever get set for ref layers.
 | ||||
|     MOZ_ASSERT(aLayer.GetReferentId()); | ||||
|   } | ||||
|   if (aParent) { | ||||
|     result |= aParent->GetEventRegionsOverride(); | ||||
|   } | ||||
|  |  | |||
							
								
								
									
										60
									
								
								gfx/layers/apz/test/mochitest/helper_override_root.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								gfx/layers/apz/test/mochitest/helper_override_root.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | |||
| <!DOCTYPE HTML> | ||||
| <html> | ||||
| <head> | ||||
|   <meta charset="utf-8"> | ||||
|   <meta name="viewport" content="width=device-width; initial-scale=1.0"> | ||||
|   <title>Simple wheel scroll cancellation</title> | ||||
|   <script type="application/javascript" src="apz_test_native_event_utils.js"></script> | ||||
|   <script type="application/javascript" src="apz_test_utils.js"></script> | ||||
|   <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script> | ||||
|   <script type="application/javascript"> | ||||
| 
 | ||||
| // Add a non-passive listener on the document, so that we have a document-level | ||||
| // APZ-aware listener, and the entire document is put in the dispatch-to-content | ||||
| // region | ||||
| document.addEventListener('wheel', function(e) { | ||||
|     dump("Wheel listener running...\n"); | ||||
| 
 | ||||
|     // spin for 2 seconds to give APZ time to scroll, if the event region override | ||||
|     // is broken and it decides not to wait for the main thread. Note that it's | ||||
|     // possible the APZ controller thread is busy for whatever reason so APZ | ||||
|     // may not scroll. That might cause this test to only fail intermittently | ||||
|     // instead of consistently if the behaviour being tested regresses. | ||||
|     var now = Date.now(); | ||||
|     while (Date.now() - now < 2000); | ||||
| 
 | ||||
|     // Cancel the scroll. If this works then we know APZ waited for this listener | ||||
|     // to run. | ||||
|     e.preventDefault(); | ||||
| 
 | ||||
|     setTimeout(function() { | ||||
|         flushApzRepaints(checkScroll); | ||||
|     }, 0); | ||||
| }, false); | ||||
| 
 | ||||
| function scrollPage() { | ||||
|   synthesizeNativeWheel(document.body, 100, 100, 0, -50); | ||||
|   dump("Finished native wheel, waiting for listener to run...\n"); | ||||
| } | ||||
| 
 | ||||
| function checkScroll() { | ||||
|   is(window.scrollY, 0, "check that the window didn't scroll"); | ||||
|   subtestDone(); | ||||
| } | ||||
| 
 | ||||
| if (window.top != window) { | ||||
|     dump("Running inside an iframe! stealing functions from window.top...\n"); | ||||
|     window.subtestDone = window.top.subtestDone; | ||||
|     window.SimpleTest = window.top.SimpleTest; | ||||
|     window.is = window.top.is; | ||||
|     window.ok = window.top.ok; | ||||
| } | ||||
| 
 | ||||
| waitUntilApzStable().then(scrollPage); | ||||
| 
 | ||||
|   </script> | ||||
| </head> | ||||
| <body style="height: 5000px; background-image: linear-gradient(green,red);"> | ||||
|   This page should not be wheel-scrollable. | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										15
									
								
								gfx/layers/apz/test/mochitest/helper_override_subdoc.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								gfx/layers/apz/test/mochitest/helper_override_subdoc.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| <!DOCTYPE HTML> | ||||
| <html> | ||||
| <head> | ||||
|   <meta charset="utf-8"> | ||||
|   <meta name="viewport" content="width=device-width; initial-scale=1.0"> | ||||
|   <title>Wheel scroll cancellation inside iframe</title> | ||||
|   <script type="application/javascript" src="apz_test_utils.js"></script> | ||||
|   <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script> | ||||
| </head> | ||||
| <body> | ||||
|     This just loads helper_override_root in an iframe, so that we test event | ||||
|     regions overriding on in-process subdocuments. | ||||
|     <iframe id="ifr" src="helper_override_root.html" onload="document.getElementById('ifr').focus()"></iframe> | ||||
| </body> | ||||
| </html> | ||||
|  | @ -21,6 +21,8 @@ | |||
|     helper_iframe2.html | ||||
|     helper_key_scroll.html | ||||
|     helper_long_tap.html | ||||
|     helper_override_root.html | ||||
|     helper_override_subdoc.html | ||||
|     helper_scroll_inactive_perspective.html | ||||
|     helper_scroll_inactive_zindex.html | ||||
|     helper_scroll_on_position_fixed.html | ||||
|  | @ -72,3 +74,5 @@ skip-if = os == 'win' && os_version == '10.0' # Bug 1404836 | |||
|   skip-if = (os == 'android') # wheel events not supported on mobile | ||||
| [test_wheel_transactions.html] | ||||
|   skip-if = (os == 'android') # wheel events not supported on mobile | ||||
| [test_group_overrides.html] | ||||
|   skip-if = (os == 'android') # wheel events not supported on mobile | ||||
|  |  | |||
							
								
								
									
										37
									
								
								gfx/layers/apz/test/mochitest/test_group_overrides.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								gfx/layers/apz/test/mochitest/test_group_overrides.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | |||
| <!DOCTYPE HTML> | ||||
| <html> | ||||
| <head> | ||||
|   <meta charset="utf-8"> | ||||
|   <title>Various tests for event regions overrides</title> | ||||
|   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> | ||||
|   <script type="application/javascript" src="apz_test_utils.js"></script> | ||||
|   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> | ||||
|   <script type="application/javascript"> | ||||
| 
 | ||||
| var prefs = [ | ||||
|   // turn off smooth scrolling so that we don't have to wait for | ||||
|   // APZ animations to finish before sampling the scroll offset | ||||
|   ['general.smoothScroll', false], | ||||
|   // Increase the content response timeout because these tests do preventDefault | ||||
|   // and we want to make sure APZ actually waits for them. | ||||
|   ['apz.content_response_timeout', 10000], | ||||
| ] | ||||
| 
 | ||||
| var subtests = [ | ||||
|   {'file': 'helper_override_root.html', 'prefs': prefs}, | ||||
|   {'file': 'helper_override_subdoc.html', 'prefs': prefs}, | ||||
| ]; | ||||
| 
 | ||||
| if (isApzEnabled()) { | ||||
|   SimpleTest.waitForExplicitFinish(); | ||||
|   window.onload = function() { | ||||
|     runSubtestsSeriallyInFreshWindows(subtests) | ||||
|     .then(SimpleTest.finish); | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
|   </script> | ||||
| </head> | ||||
| <body> | ||||
| </body> | ||||
| </html> | ||||
|  | @ -609,10 +609,6 @@ LayerTransactionParent::SetLayerAttributes(const OpSetLayerAttributes& aOp) | |||
|     containerLayer->SetInheritedScale(attrs.inheritedXScale(), attrs.inheritedYScale()); | ||||
|     containerLayer->SetScaleToResolution(attrs.scaleToResolution(), | ||||
|                                          attrs.presShellResolution()); | ||||
|     if (attrs.eventRegionsOverride() != containerLayer->GetEventRegionsOverride()) { | ||||
|       UpdateHitTestingTree(layer, "event regions override changed"); | ||||
|       containerLayer->SetEventRegionsOverride(attrs.eventRegionsOverride()); | ||||
|     } | ||||
|     break; | ||||
|   } | ||||
|   case Specific::TColorLayerAttributes: { | ||||
|  |  | |||
|  | @ -287,7 +287,6 @@ struct ContainerLayerAttributes { | |||
|   float inheritedYScale; | ||||
|   float presShellResolution; | ||||
|   bool scaleToResolution; | ||||
|   EventRegionsOverride eventRegionsOverride; | ||||
| }; | ||||
| 
 | ||||
| struct GlyphArray | ||||
|  | @ -303,8 +302,6 @@ struct ColorLayerAttributes     { LayerColor color; IntRect bounds; }; | |||
| struct CanvasLayerAttributes    { SamplingFilter samplingFilter; IntRect bounds; }; | ||||
| struct RefLayerAttributes { | ||||
|   uint64_t id; | ||||
|   // TODO: Once bug 1132895 is fixed we shouldn't need to propagate the override | ||||
|   // explicitly here. | ||||
|   EventRegionsOverride eventRegionsOverride; | ||||
| }; | ||||
| struct ImageLayerAttributes     { SamplingFilter samplingFilter; IntSize scaleToSize; ScaleMode scaleMode; }; | ||||
|  |  | |||
|  | @ -80,12 +80,6 @@ WebRenderCommandBuilder::BuildWebRenderCommands(wr::DisplayListBuilder& aBuilder | |||
|     // Make a "root" layer data that has everything else as descendants
 | ||||
|     mLayerScrollData.emplace_back(); | ||||
|     mLayerScrollData.back().InitializeRoot(mLayerScrollData.size() - 1); | ||||
|     if (aDisplayListBuilder->IsBuildingLayerEventRegions()) { | ||||
|       nsIPresShell* shell = aDisplayListBuilder->RootReferenceFrame()->PresShell(); | ||||
|       if (nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(shell)) { | ||||
|         mLayerScrollData.back().SetEventRegionsOverride(EventRegionsOverride::ForceDispatchToContent); | ||||
|       } | ||||
|     } | ||||
|     auto callback = [&aScrollData](FrameMetrics::ViewID aScrollId) -> bool { | ||||
|       return aScrollData.HasMetadataFor(aScrollId); | ||||
|     }; | ||||
|  |  | |||
|  | @ -225,24 +225,6 @@ WebRenderLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags) | |||
|   return true; | ||||
| } | ||||
| 
 | ||||
| /*static*/ int32_t | ||||
| PopulateScrollData(WebRenderScrollData& aTarget, Layer* aLayer) | ||||
| { | ||||
|   MOZ_ASSERT(aLayer); | ||||
| 
 | ||||
|   // We want to allocate a WebRenderLayerScrollData object for this layer,
 | ||||
|   // but don't keep a pointer to it since it might get memmove'd during the
 | ||||
|   // recursion below. Instead keep the index and get the pointer later.
 | ||||
|   size_t index = aTarget.AddNewLayerData(); | ||||
| 
 | ||||
|   int32_t descendants = 0; | ||||
|   for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) { | ||||
|     descendants += PopulateScrollData(aTarget, child); | ||||
|   } | ||||
|   aTarget.GetLayerDataMutable(index)->Initialize(aTarget, aLayer, descendants); | ||||
|   return descendants + 1; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| WebRenderLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback, | ||||
|                                       void* aCallbackData, | ||||
|  |  | |||
|  | @ -32,37 +32,6 @@ WebRenderLayerScrollData::~WebRenderLayerScrollData() | |||
| { | ||||
| } | ||||
| 
 | ||||
| void | ||||
| WebRenderLayerScrollData::Initialize(WebRenderScrollData& aOwner, | ||||
|                                      Layer* aLayer, | ||||
|                                      int32_t aDescendantCount) | ||||
| { | ||||
|   MOZ_ASSERT(aDescendantCount >= 0); // Ensure value is valid
 | ||||
|   MOZ_ASSERT(mDescendantCount == -1); // Don't allow re-setting an already set value
 | ||||
|   mDescendantCount = aDescendantCount; | ||||
| 
 | ||||
|   MOZ_ASSERT(aLayer); | ||||
|   for (uint32_t i = 0; i < aLayer->GetScrollMetadataCount(); i++) { | ||||
|     mScrollIds.AppendElement(aOwner.AddMetadata(aLayer->GetScrollMetadata(i))); | ||||
|   } | ||||
| 
 | ||||
|   mTransform = aLayer->GetTransform(); | ||||
|   mTransformIsPerspective = aLayer->GetTransformIsPerspective(); | ||||
|   mEventRegions = aLayer->GetEventRegions(); | ||||
|   mVisibleRegion = aLayer->GetVisibleRegion(); | ||||
|   mReferentId = aLayer->AsRefLayer() | ||||
|       ? Some(aLayer->AsRefLayer()->GetReferentId()) | ||||
|       : Nothing(); | ||||
|   mEventRegionsOverride = aLayer->AsContainerLayer() | ||||
|       ? aLayer->AsContainerLayer()->GetEventRegionsOverride() | ||||
|       : EventRegionsOverride::NoOverride; | ||||
|   mScrollThumbData = aLayer->GetScrollThumbData(); | ||||
|   mScrollbarAnimationId = aLayer->GetCompositorAnimationsId(); | ||||
|   mScrollbarTargetContainerId = aLayer->GetScrollbarTargetContainerId(); | ||||
|   mIsScrollbarContainer = aLayer->IsScrollbarContainer(); | ||||
|   mFixedPosScrollContainerId = aLayer->GetFixedPositionScrollContainerId(); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| WebRenderLayerScrollData::InitializeRoot(int32_t aDescendantCount) | ||||
| { | ||||
|  |  | |||
|  | @ -42,12 +42,6 @@ public: | |||
|   WebRenderLayerScrollData(); // needed for IPC purposes
 | ||||
|   ~WebRenderLayerScrollData(); | ||||
| 
 | ||||
|   // Actually initialize the object. This is not done during the constructor
 | ||||
|   // for optimization purposes (the call site is hard to write efficiently
 | ||||
|   // if we do this in the constructor).
 | ||||
|   void Initialize(WebRenderScrollData& aOwner, | ||||
|                   Layer* aLayer, | ||||
|                   int32_t aDescendantCount); | ||||
|   void InitializeRoot(int32_t aDescendantCount); | ||||
|   void Initialize(WebRenderScrollData& aOwner, | ||||
|                   nsDisplayItem* aItem, | ||||
|  |  | |||
|  | @ -70,6 +70,8 @@ var gFunctions = [ | |||
|   [2, (n) => n%10==1&&n%100!=11?0:1], | ||||
|   // 16: Breton
 | ||||
|   [5, (n) => n%10==1&&n%100!=11&&n%100!=71&&n%100!=91?0:n%10==2&&n%100!=12&&n%100!=72&&n%100!=92?1:(n%10==3||n%10==4||n%10==9)&&n%100!=13&&n%100!=14&&n%100!=19&&n%100!=73&&n%100!=74&&n%100!=79&&n%100!=93&&n%100!=94&&n%100!=99?2:n%1000000==0&&n!=0?3:4], | ||||
|   // 17: Shuar
 | ||||
|   [2, (n) => n!=0?1:0], | ||||
| ]; | ||||
| 
 | ||||
| this.PluralForm = { | ||||
|  |  | |||
|  | @ -589,6 +589,40 @@ function run_test() | |||
|     5,5,5,5,5,5,5,5,5,5, | ||||
|     5,1,2,3,3,5,5,5,5,3, | ||||
|     5,5,5,5,5,5,5,5,5,5, | ||||
|   ], [ | ||||
|     // 17: Shuar 0-9, 10-19, ..., 90-99
 | ||||
|     1,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     // 100-109, 110-119, ..., 190-199
 | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     // 200-209, 210-219, ..., 290-299
 | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|     2,2,2,2,2,2,2,2,2,2, | ||||
|   ]]; | ||||
| 
 | ||||
|   for (let [rule, expect] of allExpect.entries()) { | ||||
|  |  | |||
|  | @ -3780,6 +3780,9 @@ nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame, | |||
| 
 | ||||
|       builder.SetVisibleRect(visibleRect); | ||||
|       builder.SetIsBuilding(true); | ||||
|       builder.SetAncestorHasApzAwareEventHandler( | ||||
|           builder.IsBuildingLayerEventRegions() && | ||||
|           nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell)); | ||||
| 
 | ||||
|       const bool paintedPreviously = | ||||
|         aFrame->HasProperty(nsIFrame::ModifiedFrameList()); | ||||
|  |  | |||
|  | @ -2528,6 +2528,10 @@ public: | |||
| static void | ||||
| CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) | ||||
| { | ||||
|   if (aBuilder->GetAncestorHasApzAwareEventHandler()) { | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   nsIContent* content = aFrame->GetContent(); | ||||
|   if (!content) { | ||||
|     return; | ||||
|  |  | |||
|  | @ -447,12 +447,6 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder, | |||
|     needsOwnLayer = true; | ||||
|   } | ||||
| 
 | ||||
|   if (!needsOwnLayer && aBuilder->IsBuildingLayerEventRegions() && | ||||
|       nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell)) | ||||
|   { | ||||
|     needsOwnLayer = true; | ||||
|   } | ||||
| 
 | ||||
|   if (aBuilder->IsRetainingDisplayList()) { | ||||
|     // The value of needsOwnLayer can change between builds without
 | ||||
|     // an invalidation recorded for this frame (like if the root
 | ||||
|  | @ -505,7 +499,11 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder, | |||
|               ? nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent()) | ||||
|               : aBuilder->GetCurrentScrollParentId()); | ||||
| 
 | ||||
|       aBuilder->SetAncestorHasApzAwareEventHandler(false); | ||||
|       bool hasDocumentLevelListenersForApzAwareEvents = | ||||
|           aBuilder->IsBuildingLayerEventRegions() && | ||||
|           nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell); | ||||
| 
 | ||||
|       aBuilder->SetAncestorHasApzAwareEventHandler(hasDocumentLevelListenersForApzAwareEvents); | ||||
|       subdocRootFrame-> | ||||
|         BuildDisplayListForStackingContext(aBuilder, &childItems); | ||||
|     } | ||||
|  |  | |||
|  | @ -374,8 +374,8 @@ nsDisplayRemote::BuildLayer(nsDisplayListBuilder* aBuilder, | |||
|                             const ContainerLayerParameters& aContainerParameters) | ||||
| { | ||||
|   RefPtr<Layer> layer = mRemoteFrame->BuildLayer(aBuilder, mFrame, aManager, this, aContainerParameters); | ||||
|   if (layer && layer->AsContainerLayer()) { | ||||
|     layer->AsContainerLayer()->SetEventRegionsOverride(mEventRegionsOverride); | ||||
|   if (layer && layer->AsRefLayer()) { | ||||
|     layer->AsRefLayer()->SetEventRegionsOverride(mEventRegionsOverride); | ||||
|   } | ||||
|   return layer.forget(); | ||||
| } | ||||
|  |  | |||
|  | @ -2481,12 +2481,6 @@ already_AddRefed<LayerManager> nsDisplayList::PaintRoot(nsDisplayListBuilder* aB | |||
|                        1.0f/containerParameters.mYScale); | ||||
|     root->SetScaleToResolution(presShell->ScaleToResolution(), | ||||
|         containerParameters.mXScale); | ||||
|     if (aBuilder->IsBuildingLayerEventRegions() && | ||||
|         nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell)) { | ||||
|       root->SetEventRegionsOverride(EventRegionsOverride::ForceDispatchToContent); | ||||
|     } else { | ||||
|       root->SetEventRegionsOverride(EventRegionsOverride::NoOverride); | ||||
|     } | ||||
| 
 | ||||
|     auto callback = [root](FrameMetrics::ViewID aScrollId) -> bool { | ||||
|       return nsLayoutUtils::ContainsMetricsWithId(root, aScrollId); | ||||
|  | @ -6861,9 +6855,6 @@ nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder, | |||
|     , mSubDocFrame(aSubDocFrame) | ||||
| { | ||||
|   MOZ_COUNT_CTOR(nsDisplaySubDocument); | ||||
|   mForceDispatchToContentRegion = | ||||
|     aBuilder->IsBuildingLayerEventRegions() && | ||||
|     nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(aFrame->PresShell()); | ||||
| 
 | ||||
|   // The SubDocument display item is conceptually outside the viewport frame,
 | ||||
|   // so in cases where the viewport frame is an AGR, the SubDocument's AGR
 | ||||
|  | @ -6879,17 +6870,6 @@ nsDisplaySubDocument::~nsDisplaySubDocument() { | |||
| } | ||||
| #endif | ||||
| 
 | ||||
| already_AddRefed<Layer> | ||||
| nsDisplaySubDocument::BuildLayer(nsDisplayListBuilder* aBuilder, | ||||
|                                  LayerManager* aManager, | ||||
|                                  const ContainerLayerParameters& aContainerParameters) { | ||||
|   RefPtr<Layer> layer = nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, aContainerParameters); | ||||
|   layer->AsContainerLayer()->SetEventRegionsOverride(mForceDispatchToContentRegion | ||||
|     ? EventRegionsOverride::ForceDispatchToContent | ||||
|     : EventRegionsOverride::NoOverride); | ||||
|   return layer.forget(); | ||||
| } | ||||
| 
 | ||||
| UniquePtr<ScrollMetadata> | ||||
| nsDisplaySubDocument::ComputeScrollMetadata(LayerManager* aLayerManager, | ||||
|                                             const ContainerLayerParameters& aContainerParameters) | ||||
|  |  | |||
|  | @ -701,7 +701,7 @@ public: | |||
|     return CurrentPresShellState()->mInsidePointerEventsNoneDoc; | ||||
|   } | ||||
| 
 | ||||
|   bool GetAncestorHasApzAwareEventHandler() { return mAncestorHasApzAwareEventHandler; } | ||||
|   bool GetAncestorHasApzAwareEventHandler() const { return mAncestorHasApzAwareEventHandler; } | ||||
|   void SetAncestorHasApzAwareEventHandler(bool aValue) | ||||
|   { | ||||
|     mAncestorHasApzAwareEventHandler = aValue; | ||||
|  | @ -5111,10 +5111,6 @@ public: | |||
|   virtual ~nsDisplaySubDocument(); | ||||
| #endif | ||||
| 
 | ||||
|   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder, | ||||
|                                              LayerManager* aManager, | ||||
|                                              const ContainerLayerParameters& aContainerParameters) override; | ||||
| 
 | ||||
|   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, | ||||
|                            bool* aSnap) const override; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1159,4 +1159,4 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = { | |||
| 
 | ||||
| static const int32_t kUnknownId = -1; | ||||
| 
 | ||||
| static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1518637560032000); | ||||
| static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1518723892807000); | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -8,7 +8,7 @@ | |||
| /*****************************************************************************/ | ||||
| 
 | ||||
| #include <stdint.h>
 | ||||
| const PRTime gPreloadListExpirationTime = INT64_C(1521056747051000); | ||||
| const PRTime gPreloadListExpirationTime = INT64_C(1521143078420000); | ||||
| %% | ||||
| 0-1.party, 1 | ||||
| 0.me.uk, 1 | ||||
|  | @ -126,7 +126,9 @@ const PRTime gPreloadListExpirationTime = INT64_C(1521056747051000); | |||
| 13318522.com, 1 | ||||
| 1359826938.rsc.cdn77.org, 1 | ||||
| 13826145000.com, 1 | ||||
| 1391kj.com, 1 | ||||
| 1395kj.com, 1 | ||||
| 1396.cc, 1 | ||||
| 1453914078.rsc.cdn77.org, 1 | ||||
| 1464424382.rsc.cdn77.org, 1 | ||||
| 14it.de, 1 | ||||
|  | @ -368,7 +370,6 @@ const PRTime gPreloadListExpirationTime = INT64_C(1521056747051000); | |||
| 50lakeshore.com, 1 | ||||
| 50north.de, 1 | ||||
| 50plusnet.nl, 1 | ||||
| 513vpn.net, 1 | ||||
| 525.info, 1 | ||||
| 52neptune.com, 1 | ||||
| 5432.cc, 1 | ||||
|  | @ -1975,6 +1976,7 @@ arijitdg.net, 1 | |||
| arikar.eu, 1 | ||||
| arima.co.ke, 1 | ||||
| arinflatablefun.co.uk, 1 | ||||
| aristilabs.com, 0 | ||||
| aristocrates.co, 1 | ||||
| aristocratps.com, 1 | ||||
| aritec-la.com, 1 | ||||
|  | @ -2062,6 +2064,7 @@ artionet.ch, 1 | |||
| artisanhd.com, 1 | ||||
| artisans-libres.com, 1 | ||||
| artisense.de, 1 | ||||
| artisphere.ch, 1 | ||||
| artisticedgegranite.net, 1 | ||||
| artlantis.nl, 1 | ||||
| artleading.ru, 1 | ||||
|  | @ -3102,7 +3105,6 @@ besthotsales.com, 1 | |||
| bestlashesandbrows.com, 1 | ||||
| bestlashesandbrows.hu, 1 | ||||
| bestleftwild.com, 1 | ||||
| bestmodels.su, 1 | ||||
| bestmotherfucking.website, 1 | ||||
| bestoliveoils.com, 1 | ||||
| bestpartyhire.com, 1 | ||||
|  | @ -3367,6 +3369,7 @@ birminghamsunset.com, 1 | |||
| birthdaytip.com, 1 | ||||
| birthmatters.us, 1 | ||||
| birzan.org, 1 | ||||
| biscoint.io, 1 | ||||
| biscuits-rec.com, 1 | ||||
| biscuits-shop.com, 1 | ||||
| bismarck-tb.de, 1 | ||||
|  | @ -4214,11 +4217,13 @@ brunoramos.org, 1 | |||
| brunosouza.org, 1 | ||||
| bruun.co, 1 | ||||
| bryankaplan.com, 1 | ||||
| bryanquigley.com, 1 | ||||
| bs-network.net, 1 | ||||
| bs-security.com, 1 | ||||
| bs.sb, 1 | ||||
| bs.to, 1 | ||||
| bs12v.ru, 1 | ||||
| bsagan.fr, 1 | ||||
| bsalyzer.com, 1 | ||||
| bsc-rietz.at, 1 | ||||
| bsc01.dyndns.org, 1 | ||||
|  | @ -4374,7 +4379,6 @@ busindre.com, 1 | |||
| business-garden.com, 1 | ||||
| business.facebook.com, 0 | ||||
| business.medbank.com.mt, 1 | ||||
| businessadviceperth.com.au, 0 | ||||
| businessamongus.com, 1 | ||||
| businesscentermarin.ch, 1 | ||||
| businessesdirectory.eu, 1 | ||||
|  | @ -4531,7 +4535,7 @@ cairnterrier.com.br, 1 | |||
| cais.de, 1 | ||||
| caitcs.com, 1 | ||||
| caja-pdf.es, 1 | ||||
| cajio.ru, 1 | ||||
| cajio.ru, 0 | ||||
| cajunuk.co.uk, 1 | ||||
| cake-time.co.uk, 1 | ||||
| cakestart.net, 1 | ||||
|  | @ -5050,7 +5054,6 @@ chambion.ch, 1 | |||
| chameleon-ents.co.uk, 1 | ||||
| chameth.com, 1 | ||||
| chamilo.org, 1 | ||||
| champ.dog, 1 | ||||
| champdogs.co.uk, 1 | ||||
| champdogs.com, 1 | ||||
| champicreuse.fr, 1 | ||||
|  | @ -5206,7 +5209,6 @@ chengl.com, 1 | |||
| chengtongled.com, 1 | ||||
| chenky.com, 1 | ||||
| chennien.com, 1 | ||||
| chentianyi.cn, 1 | ||||
| chenzhekl.me, 1 | ||||
| cherevoiture.com, 1 | ||||
| cherrett.digital, 1 | ||||
|  | @ -5472,7 +5474,6 @@ citizen-cam.de, 1 | |||
| citizensbankal.com, 1 | ||||
| citizenscience.gov, 1 | ||||
| citizensleague.org, 1 | ||||
| citizenspact.eu, 1 | ||||
| citizing.org, 1 | ||||
| citra-emu.org, 1 | ||||
| citrusui.me, 1 | ||||
|  | @ -5884,6 +5885,7 @@ colorblindprogramming.com, 1 | |||
| colorbrush.ru, 1 | ||||
| colorcentertoner.com.br, 1 | ||||
| colorcodedlyrics.com, 1 | ||||
| colorectalcompounding.com, 1 | ||||
| coloringnotebook.com, 1 | ||||
| coloristcafe.com, 1 | ||||
| colorsbycarin.com, 1 | ||||
|  | @ -5909,6 +5911,7 @@ comeseetv.com, 1 | |||
| comestoarra.com, 1 | ||||
| cometbot.cf, 1 | ||||
| cometcache.com, 1 | ||||
| cometonovascotia.ca, 1 | ||||
| comff.net, 1 | ||||
| comfintouch.com, 1 | ||||
| comflores.com.br, 1 | ||||
|  | @ -5936,7 +5939,6 @@ communitycodeofconduct.com, 1 | |||
| communityflow.info, 1 | ||||
| communote.net, 1 | ||||
| comocurarlashemorroides.org, 1 | ||||
| comocurarlashemorroidesya.com, 1 | ||||
| comodesinflamarlashemorroides.org, 1 | ||||
| comodo.nl, 1 | ||||
| comodormirmasrapido.com, 1 | ||||
|  | @ -6242,6 +6244,7 @@ cousincouples.com, 1 | |||
| covbounce.co.uk, 1 | ||||
| cove.sh, 1 | ||||
| covenantoftheriver.org, 1 | ||||
| coverdat.com, 1 | ||||
| covermytrip.com.au, 1 | ||||
| covershousing.nl, 1 | ||||
| covoiturage.fr, 0 | ||||
|  | @ -7378,6 +7381,7 @@ devdesco.com, 1 | |||
| devdom.io, 1 | ||||
| devdoodle.net, 1 | ||||
| devel.cz, 1 | ||||
| develop.cool, 1 | ||||
| developer.mydigipass.com, 0 | ||||
| developerfair.com, 1 | ||||
| developermail.io, 1 | ||||
|  | @ -7411,7 +7415,6 @@ devops-survey.com, 1 | |||
| devops.moe, 1 | ||||
| devpgsv.com, 1 | ||||
| devpsy.info, 1 | ||||
| devstaff.gr, 1 | ||||
| devyn.ca, 1 | ||||
| devzero.io, 1 | ||||
| dewalch.net, 1 | ||||
|  | @ -8118,7 +8121,6 @@ drlazarina.net, 1 | |||
| drms.us, 1 | ||||
| drobniuch.pl, 0 | ||||
| drogoz.moe, 1 | ||||
| drogueriaelbarco.com, 1 | ||||
| droidapp.nl, 1 | ||||
| droidgyan.com, 1 | ||||
| droidhere.com, 1 | ||||
|  | @ -8199,11 +8201,11 @@ duelsow.eu, 1 | |||
| duernberg.at, 1 | ||||
| duesee.org, 1 | ||||
| dufrei.com, 1 | ||||
| dugnet.com, 1 | ||||
| dugnet.io, 1 | ||||
| dugnet.net, 1 | ||||
| dugnet.org, 1 | ||||
| dugnet.tech, 1 | ||||
| dugnet.com, 0 | ||||
| dugnet.io, 0 | ||||
| dugnet.net, 0 | ||||
| dugnet.org, 0 | ||||
| dugnet.tech, 0 | ||||
| dugunedavet.com, 1 | ||||
| duh.se, 1 | ||||
| duijf.info, 1 | ||||
|  | @ -8226,7 +8228,6 @@ duncancmt.com, 1 | |||
| duncanfamilytrust.org, 1 | ||||
| duncanwinfrey.com, 1 | ||||
| dundalkdonnie.com, 1 | ||||
| dune.io, 1 | ||||
| dunea.nl, 1 | ||||
| dungeon-bbs.de, 1 | ||||
| dunmanelectric.com, 1 | ||||
|  | @ -8504,7 +8505,6 @@ edited.de, 1 | |||
| edition-bambou.com, 1 | ||||
| edition-sonblom.de, 1 | ||||
| editoraacademiacrista.com.br, 1 | ||||
| edlinus.cn, 1 | ||||
| edmundcelis.com, 1 | ||||
| edoss.co.za, 1 | ||||
| edp-collaborative.com, 1 | ||||
|  | @ -8798,7 +8798,6 @@ emailprivacytester.com, 1 | |||
| emailtools.io, 1 | ||||
| emaily.eu, 1 | ||||
| emanuelduss.ch, 1 | ||||
| emanuelemazzotta.com, 1 | ||||
| emavok.eu, 1 | ||||
| embassycargo.eu, 1 | ||||
| embellir-aroma.com, 1 | ||||
|  | @ -8823,6 +8822,7 @@ emil.click, 1 | |||
| emilong.com, 1 | ||||
| emilstahl.dk, 1 | ||||
| emilvarga.com, 1 | ||||
| emilyjohnson.ga, 1 | ||||
| emilyshepherd.me, 1 | ||||
| eminhuseynov.com, 1 | ||||
| emirabiz.com, 0 | ||||
|  | @ -9018,7 +9018,7 @@ epitesz.co, 1 | |||
| epizentrum.work, 1 | ||||
| epizentrum.works, 1 | ||||
| epmcentroitalia.it, 1 | ||||
| epoch.com, 0 | ||||
| epoch.com, 1 | ||||
| epolitiker.com, 1 | ||||
| epos-distributor.co.uk, 1 | ||||
| eposbirmingham.co.uk, 1 | ||||
|  | @ -9570,6 +9570,7 @@ fabled.com, 1 | |||
| fableforge.nl, 1 | ||||
| fabriceleroux.com, 1 | ||||
| fabriziorocca.it, 1 | ||||
| fabrysociety.org, 1 | ||||
| fabse.net, 1 | ||||
| fabulouslyyouthfulskin.com, 1 | ||||
| fabulouslyyouthfulskineyeserum.com, 1 | ||||
|  | @ -9792,7 +9793,6 @@ feastr-dev.de, 1 | |||
| feastr.de, 1 | ||||
| feastr.io, 1 | ||||
| featherweightlabs.com, 1 | ||||
| featuredmen.com, 1 | ||||
| fecik.sk, 1 | ||||
| fed51.com, 1 | ||||
| federalinvestments.gov, 1 | ||||
|  | @ -10628,7 +10628,6 @@ front-end.dog, 1 | |||
| fronteers.nl, 0 | ||||
| frontline.cloud, 1 | ||||
| frontline6.com, 1 | ||||
| fropky.com, 1 | ||||
| frostbytes.net, 1 | ||||
| frostwarning.com, 1 | ||||
| frosty-gaming.xyz, 1 | ||||
|  | @ -10772,7 +10771,6 @@ furtivelook.com, 1 | |||
| fusa-miyamoto.jp, 1 | ||||
| fuseos.net, 1 | ||||
| fushee.com, 1 | ||||
| fuskator.com, 1 | ||||
| fussball-xxl.de, 1 | ||||
| fussell.io, 1 | ||||
| futbolvivo.tv, 1 | ||||
|  | @ -11360,7 +11358,7 @@ gixtools.uk, 1 | |||
| gizmo.ovh, 1 | ||||
| gj-bochum.de, 1 | ||||
| gjcampbell.co.uk, 1 | ||||
| gjengset.com, 0 | ||||
| gjengset.com, 1 | ||||
| gjspunk.de, 0 | ||||
| gjung.com, 0 | ||||
| gkralik.eu, 1 | ||||
|  | @ -11549,7 +11547,6 @@ gorod74.ru, 0 | |||
| gorschenin.com, 1 | ||||
| gosccs.com, 1 | ||||
| gosciencegirls.com, 1 | ||||
| gosharewood.com, 1 | ||||
| goshawkdb.io, 1 | ||||
| goshin-group.co.jp, 1 | ||||
| gospelfollower.com, 1 | ||||
|  | @ -12070,6 +12067,7 @@ hanxv.pw, 1 | |||
| hanys.xyz, 1 | ||||
| hanzubon.jp, 1 | ||||
| hao-zhang.com, 1 | ||||
| haogoodair.ca, 1 | ||||
| haozhang.org, 1 | ||||
| hapijs.cn, 1 | ||||
| hapissl.com, 1 | ||||
|  | @ -12148,6 +12146,7 @@ hashimah.ca, 1 | |||
| hashinteractive.com, 1 | ||||
| hashish.net, 1 | ||||
| hashiura.jp, 1 | ||||
| hashnode.com, 1 | ||||
| hashplex.com, 1 | ||||
| hashru.nl, 1 | ||||
| hashworks.net, 1 | ||||
|  | @ -13394,7 +13393,7 @@ immobilien-badlippspringe.de, 1 | |||
| immobilien-wallat.de, 1 | ||||
| immobilier-nice.fr, 1 | ||||
| immobilier92.net, 1 | ||||
| immobiza.com, 1 | ||||
| immobiza.com, 0 | ||||
| immortal.run, 1 | ||||
| immunicity.st, 1 | ||||
| imokuri123.com, 1 | ||||
|  | @ -13752,7 +13751,6 @@ intraobes.com, 1 | |||
| intrasoft.com.au, 1 | ||||
| intraxia.com, 1 | ||||
| introvertedtravel.space, 1 | ||||
| intune.life, 1 | ||||
| intux.be, 0 | ||||
| intvonline.com, 1 | ||||
| intxt.net, 1 | ||||
|  | @ -13950,6 +13948,7 @@ ispweb.es, 1 | |||
| isqrl.de, 1 | ||||
| israelbizreg.com, 1 | ||||
| israkurort.com, 1 | ||||
| isreedyintheuk.com, 1 | ||||
| issasfrissa.se, 1 | ||||
| issforum.org, 1 | ||||
| issio.net, 1 | ||||
|  | @ -14440,6 +14439,7 @@ jevisite.ca, 1 | |||
| jeweet.net, 1 | ||||
| jez.nl, 1 | ||||
| jf-projects.de, 0 | ||||
| jfnllc.com, 1 | ||||
| jfr.im, 1 | ||||
| jfreitag.de, 1 | ||||
| jgid.de, 1 | ||||
|  | @ -15281,6 +15281,7 @@ kiapartscenter.net, 1 | |||
| kiapartsdepartment.com, 1 | ||||
| kiapps.ovh, 1 | ||||
| kibibit.net, 1 | ||||
| kibriscicek.net, 1 | ||||
| kick-in.nl, 1 | ||||
| kickasscanadians.ca, 1 | ||||
| kickedmycat.com, 1 | ||||
|  | @ -15433,7 +15434,9 @@ kiwipayment.com, 1 | |||
| kiwipayments.com, 1 | ||||
| kiwiplace.com, 1 | ||||
| kj-prince.com, 1 | ||||
| kj1391.com, 1 | ||||
| kj1396.net, 1 | ||||
| kj1397.com, 1 | ||||
| kjaer.io, 1 | ||||
| kjarni.cc, 1 | ||||
| kjarrval.is, 1 | ||||
|  | @ -16105,6 +16108,7 @@ lattyware.com, 1 | |||
| laubacher.io, 1 | ||||
| lauchundei.at, 1 | ||||
| laufcampus.com, 1 | ||||
| laufpix.de, 1 | ||||
| laufseminare-laufreisen.com, 1 | ||||
| lauftrainer-ausbildung.com, 1 | ||||
| lauftreff-himmelgeist.de, 1 | ||||
|  | @ -16132,7 +16136,7 @@ lavenderx.org, 1 | |||
| lavita.de, 1 | ||||
| lavitrine-une-collection.be, 1 | ||||
| lavolte.net, 1 | ||||
| lavval.com, 1 | ||||
| lavval.com, 0 | ||||
| law-peters.de, 1 | ||||
| lawformt.com, 1 | ||||
| lawn-seeds.com, 1 | ||||
|  | @ -16382,6 +16386,7 @@ lesyndicat.info, 1 | |||
| let-go.cc, 1 | ||||
| letemps.ch, 1 | ||||
| leticiagomeztagle.com, 1 | ||||
| letitfly.me, 1 | ||||
| lets-bounce.com, 1 | ||||
| lets-go-acoustic.de, 1 | ||||
| lets-ktai.jp, 1 | ||||
|  | @ -16727,6 +16732,7 @@ livepaperhelp.com, 1 | |||
| liveperformersmeeting.net, 1 | ||||
| liveregistratie.nl, 1 | ||||
| livesure.com, 1 | ||||
| livi.co, 1 | ||||
| living-space.co.nz, 1 | ||||
| living24.de, 1 | ||||
| livingforreal.com, 1 | ||||
|  | @ -17006,7 +17012,6 @@ lu.search.yahoo.com, 0 | |||
| luav.org, 1 | ||||
| lubar.me, 1 | ||||
| lubbockyounglawyers.org, 1 | ||||
| lubomirkazakov.com, 1 | ||||
| lubot.net, 0 | ||||
| luc-oberson.ch, 1 | ||||
| luca.swiss, 1 | ||||
|  | @ -17703,7 +17708,6 @@ matrixmedia.ro, 1 | |||
| matrixreq.com, 1 | ||||
| matsu-semi.com, 1 | ||||
| mattandyana.com, 1 | ||||
| mattberryman.com, 1 | ||||
| mattbsg.xyz, 1 | ||||
| mattcarr.net, 0 | ||||
| mattcoles.io, 1 | ||||
|  | @ -17793,7 +17797,6 @@ mazda626.net, 1 | |||
| maze.fr, 1 | ||||
| mazternet.ru, 1 | ||||
| mazurlabs.tk, 1 | ||||
| mazzotta.me, 1 | ||||
| mb-is.info, 1 | ||||
| mbaestlein.de, 1 | ||||
| mbainflatables.co.uk, 1 | ||||
|  | @ -17868,6 +17871,7 @@ mdcloudps.com, 1 | |||
| mdek.at, 1 | ||||
| mdewendt.de, 1 | ||||
| mdf-bis.com, 1 | ||||
| mdkr.nl, 1 | ||||
| mdma.net, 1 | ||||
| mdmed.clinic, 1 | ||||
| mdoering.de, 1 | ||||
|  | @ -18021,6 +18025,7 @@ melbourneapartments.website, 1 | |||
| melchizedek-forum.de, 1 | ||||
| meldcode-assistent.nl, 1 | ||||
| melearning.university, 0 | ||||
| meledia.com, 1 | ||||
| melenchatsmelenchiens.fr, 1 | ||||
| melerpaine.com, 1 | ||||
| melf.nl, 1 | ||||
|  | @ -18285,6 +18290,7 @@ miguia.tv, 1 | |||
| mihnea.net, 1 | ||||
| mijnkerstkaarten.be, 1 | ||||
| mijnreisoverzicht.nl, 1 | ||||
| mijnstembureau.nl, 1 | ||||
| mijntransacties.nl, 1 | ||||
| mika.cat, 1 | ||||
| mika.moe, 1 | ||||
|  | @ -18494,7 +18500,6 @@ mitzpettel.com, 1 | |||
| miui-germany.de, 1 | ||||
| miukimodafeminina.com, 1 | ||||
| mivzakim.net, 1 | ||||
| miweb.cr, 1 | ||||
| mixnshake.com, 1 | ||||
| mixposure.com, 1 | ||||
| mixtape.moe, 1 | ||||
|  | @ -18882,7 +18887,7 @@ mplicka.cz, 1 | |||
| mplusm.eu, 1 | ||||
| mpn.poker, 1 | ||||
| mpnpokertour.com, 1 | ||||
| mpreserver.com, 0 | ||||
| mpreserver.com, 1 | ||||
| mpserver12.org, 1 | ||||
| mpsgarage.com.au, 1 | ||||
| mpsoundcraft.com, 1 | ||||
|  | @ -19918,7 +19923,6 @@ nickmorri.com, 1 | |||
| nickrickard.co.uk, 1 | ||||
| nickstories.de, 1 | ||||
| niclasreich.de, 1 | ||||
| nico.one, 1 | ||||
| nico.st, 1 | ||||
| nicocourts.com, 1 | ||||
| nicoknibbe.nl, 1 | ||||
|  | @ -20128,6 +20132,7 @@ noop.ch, 1 | |||
| noordsee.de, 1 | ||||
| noorsolidarity.com, 1 | ||||
| nootropic.com, 1 | ||||
| nootropicsource.com, 1 | ||||
| nopaste.xyz, 1 | ||||
| nopaynocure.com, 1 | ||||
| nord-sud.be, 1 | ||||
|  | @ -20341,7 +20346,7 @@ nwwc.dk, 1 | |||
| nwwnetwork.net, 1 | ||||
| nxinfo.ch, 1 | ||||
| nyan.it, 1 | ||||
| nyanpasu.tv, 1 | ||||
| nyanpasu.tv, 0 | ||||
| nyantec.com, 1 | ||||
| nycoyote.org, 1 | ||||
| nydnxs.com, 1 | ||||
|  | @ -22208,6 +22213,7 @@ postdarwinian.com, 1 | |||
| postdarwinism.com, 1 | ||||
| postdeck.de, 1 | ||||
| posteo.de, 0 | ||||
| posterspy.com, 1 | ||||
| postfalls-naturopathic.com, 1 | ||||
| postfinance.ch, 1 | ||||
| postmatescode.com, 1 | ||||
|  | @ -22302,7 +22308,6 @@ preisser-it.de, 1 | |||
| preisser.it, 1 | ||||
| prekladysanca.cz, 1 | ||||
| preloaded-hsts.badssl.com, 1 | ||||
| prelogica.com.br, 1 | ||||
| preludes.org, 1 | ||||
| prelved.com, 1 | ||||
| prelved.es, 1 | ||||
|  | @ -22541,6 +22546,7 @@ proposalonline.com, 1 | |||
| propr.no, 1 | ||||
| propseller.com, 1 | ||||
| proslimdiets.com, 1 | ||||
| prosocialmachines.com, 1 | ||||
| prospanek.cz, 1 | ||||
| prospo.co, 1 | ||||
| prostohobby.ru, 1 | ||||
|  | @ -22563,7 +22569,7 @@ proust.ch, 0 | |||
| proust.media, 0 | ||||
| proustmedia.de, 0 | ||||
| provectus.de, 1 | ||||
| proveits.me, 1 | ||||
| proveits.me, 0 | ||||
| provence-appartements.com, 1 | ||||
| provision-isr.nl, 1 | ||||
| provisionaldriving.com, 1 | ||||
|  | @ -23427,7 +23433,6 @@ replicaswiss.nl, 1 | |||
| report-incident.de, 1 | ||||
| report-to.io, 1 | ||||
| report-uri.com, 1 | ||||
| report-uri.io, 1 | ||||
| report-url.com, 1 | ||||
| report-url.io, 1 | ||||
| reported.ly, 1 | ||||
|  | @ -23932,6 +23937,7 @@ royalty-market.com, 1 | |||
| royalvisiongroup.com, 1 | ||||
| royzez.com, 1 | ||||
| rozalisbengal.ro, 1 | ||||
| rozalynne-dawn.ga, 1 | ||||
| rozhodce.cz, 1 | ||||
| rpadovani.com, 1 | ||||
| rpasafrica.com, 1 | ||||
|  | @ -24184,6 +24190,7 @@ sahkotyot.eu, 1 | |||
| said.id, 1 | ||||
| said.my.id, 1 | ||||
| saier.me, 1 | ||||
| saifoundation.in, 1 | ||||
| saigonflowers.com, 1 | ||||
| saigonstar.de, 1 | ||||
| saikarra.com, 1 | ||||
|  | @ -25290,7 +25297,6 @@ sidium.de, 1 | |||
| sidnicio.us, 1 | ||||
| sidonge.com, 1 | ||||
| sidongkim.com, 1 | ||||
| sidpod.ru, 1 | ||||
| siebeve.be, 1 | ||||
| siegemund-frankfurt.de, 1 | ||||
| sieh.es, 1 | ||||
|  | @ -25407,6 +25413,7 @@ sim-karten.net, 1 | |||
| sim-sim.appspot.com, 1 | ||||
| sim4seed.org, 1 | ||||
| simam.de, 1 | ||||
| simbeton.nl, 1 | ||||
| simbolo.co.uk, 0 | ||||
| simeonoff.ninja, 1 | ||||
| simetal.ch, 1 | ||||
|  | @ -26280,7 +26287,6 @@ srbija-nekretnine.org, 1 | |||
| src.fedoraproject.org, 1 | ||||
| srchub.org, 1 | ||||
| srinivasan.io, 1 | ||||
| sritest.io, 1 | ||||
| sro.center, 1 | ||||
| srolim.com, 1 | ||||
| srrdb.com, 1 | ||||
|  | @ -26470,7 +26476,7 @@ steenackers.be, 1 | |||
| stefan-bayer.eu, 1 | ||||
| stefan-schlueter.de, 1 | ||||
| stefanbayer.de, 1 | ||||
| stefanovski.io, 0 | ||||
| stefanovski.io, 1 | ||||
| stefany.eu, 1 | ||||
| steffi-in-australien.com, 1 | ||||
| steidlewirt.de, 1 | ||||
|  | @ -26545,7 +26551,6 @@ stevenz.xyz, 1 | |||
| stevesdrivingschooltyneside.com, 1 | ||||
| stewartswines.com, 1 | ||||
| stewonet.nl, 1 | ||||
| steyaert.be, 1 | ||||
| stfw.info, 1 | ||||
| stichtingliab.nl, 1 | ||||
| stichtingscholierenvervoerzeeland.nl, 1 | ||||
|  | @ -26606,6 +26611,7 @@ stonemanbrasil.com.br, 1 | |||
| stony.com, 1 | ||||
| stonystratford.org, 1 | ||||
| stopakwardhandshakes.org, 1 | ||||
| stopbreakupnow.org, 1 | ||||
| stopbullying.gov, 1 | ||||
| stopfraud.gov, 1 | ||||
| stopthethyroidmadness.com, 1 | ||||
|  | @ -26644,7 +26650,6 @@ streamchan.org, 1 | |||
| streamdesk.ca, 1 | ||||
| streamer.tips, 1 | ||||
| streamlineautogroup.com, 1 | ||||
| streampanel.net, 1 | ||||
| streams.dyndns.org, 1 | ||||
| streamthemeeting.com, 1 | ||||
| streamzilla.com, 1 | ||||
|  | @ -28017,6 +28022,7 @@ tik.help, 1 | |||
| tiki-god.co.uk, 1 | ||||
| tiledailyshop.com, 1 | ||||
| tileyourvisit.pt, 1 | ||||
| tiliaze.biz, 1 | ||||
| tiliaze.info, 1 | ||||
| tiliaze.net, 1 | ||||
| till.im, 1 | ||||
|  | @ -28053,8 +28059,10 @@ timroes.de, 1 | |||
| timstoffel.net, 0 | ||||
| timtaubert.de, 1 | ||||
| timtelfer.com, 1 | ||||
| timtj.ca, 1 | ||||
| timvandekamp.nl, 1 | ||||
| timvivian.ca, 1 | ||||
| timweb.ca, 1 | ||||
| timysewyn.be, 1 | ||||
| tinastahlschmidt.de, 1 | ||||
| tinf15b4.de, 1 | ||||
|  | @ -28443,6 +28451,7 @@ towaway.ru, 1 | |||
| townandcountryus.com, 1 | ||||
| townhousedevelopments.com.au, 1 | ||||
| townhouseregister.com.au, 1 | ||||
| townofbridgewater.ca, 1 | ||||
| towywebdesigns.uk, 1 | ||||
| tox.im, 1 | ||||
| toxicip.com, 1 | ||||
|  | @ -28483,7 +28492,6 @@ trackdays4fun.com, 1 | |||
| trackdomains.com, 1 | ||||
| trackersimulator.org, 1 | ||||
| trackeye.dk, 1 | ||||
| trackmeet.io, 1 | ||||
| trackrecordpro.co.uk, 1 | ||||
| tractorpumps.com, 1 | ||||
| trade.gov.uk, 1 | ||||
|  | @ -29221,6 +29229,7 @@ urbanietz-immobilien.de, 1 | |||
| urbanmelbourne.info, 1 | ||||
| urbannewsservice.com, 1 | ||||
| urbansparrow.in, 1 | ||||
| urbanstylestaging.com, 1 | ||||
| urbanwildlifealliance.org, 1 | ||||
| urbexdk.nl, 1 | ||||
| urcentral.com, 1 | ||||
|  | @ -29511,7 +29520,6 @@ vendigital.com, 1 | |||
| vendorconnect.nyc, 1 | ||||
| venicerealdeal.com, 1 | ||||
| venmos.com, 1 | ||||
| venoom.eu, 1 | ||||
| ventesprivees-fr.com, 1 | ||||
| ventizo.com, 1 | ||||
| venturavwparts.com, 1 | ||||
|  | @ -29976,7 +29984,7 @@ wadvisor.com, 1 | |||
| waelisch.de, 1 | ||||
| waelti.xxx, 1 | ||||
| wafa4hw.com, 1 | ||||
| wafairhaven.com.au, 0 | ||||
| wafairhaven.com.au, 1 | ||||
| waffle.at, 1 | ||||
| wafni.com, 1 | ||||
| wahhoi.net, 0 | ||||
|  | @ -30080,6 +30088,7 @@ watermonitor.gov, 1 | |||
| watersb.org, 1 | ||||
| watertrails.io, 1 | ||||
| watsonwork.me, 1 | ||||
| wattechweb.com, 1 | ||||
| wave-ola.es, 1 | ||||
| wavesboardshop.com, 1 | ||||
| wavesoftime.com, 1 | ||||
|  | @ -30909,6 +30918,7 @@ wubify.com, 1 | |||
| wuchipc.com, 1 | ||||
| wuerfel.wf, 1 | ||||
| wuerfelmail.de, 1 | ||||
| wufu.org, 0 | ||||
| wug.jp, 1 | ||||
| wug.news, 1 | ||||
| wuji.cz, 1 | ||||
|  | @ -30998,7 +31008,6 @@ www.theguardian.com, 1 | |||
| www.therapynotes.com, 1 | ||||
| www.tinfoilsecurity.com, 0 | ||||
| www.torproject.org, 0 | ||||
| www.tumblr.com, 0 | ||||
| www.twitter.com, 0 | ||||
| www.united.com, 1 | ||||
| www.usaa.com, 0 | ||||
|  | @ -31653,9 +31662,9 @@ zabszk.net, 1 | |||
| zacarias.com.ar, 1 | ||||
| zacavi.com.br, 1 | ||||
| zach.codes, 1 | ||||
| zacharopoulos.eu, 1 | ||||
| zacharopoulos.me, 1 | ||||
| zacharopoulos.org, 1 | ||||
| zacharopoulos.eu, 0 | ||||
| zacharopoulos.me, 0 | ||||
| zacharopoulos.org, 0 | ||||
| zachborboa.com, 1 | ||||
| zachgibbens.org, 1 | ||||
| zachpeters.org, 1 | ||||
|  | @ -31709,7 +31718,6 @@ zdbl.de, 1 | |||
| zdenekspacek.cz, 1 | ||||
| zdorovayasimya.com, 1 | ||||
| zdrojak.cz, 1 | ||||
| zdx.ch, 1 | ||||
| ze3kr.com, 1 | ||||
| zebbra.ro, 1 | ||||
| zebedeescastles.co.uk, 1 | ||||
|  | @ -31925,7 +31933,6 @@ zuppy.pm, 1 | |||
| zuralski.net, 1 | ||||
| zurgl.com, 1 | ||||
| zurickrelogios.com.br, 1 | ||||
| zurret.de, 1 | ||||
| zusjesvandenbos.nl, 1 | ||||
| zutsu-raku.com, 1 | ||||
| zuviel.space, 1 | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| 
 | ||||
| use cssparser::ToCss; | ||||
| use parser::SelectorImpl; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::fmt; | ||||
| 
 | ||||
| #[derive(Clone, Eq, PartialEq)] | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ use precomputed_hash::PrecomputedHash; | |||
| use servo_arc::ThinArc; | ||||
| use sink::Push; | ||||
| use smallvec::SmallVec; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::borrow::{Borrow, Cow}; | ||||
| use std::fmt::{self, Display, Debug, Write}; | ||||
| use std::iter::Rev; | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ use selectors::attr::AttrSelectorOperation; | |||
| use servo_arc::Arc; | ||||
| use servo_url::ServoUrl; | ||||
| use shared_lock::Locked; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::str::FromStr; | ||||
| use str::{HTML_SPACE_CHARACTERS, read_exponent, read_fraction}; | ||||
| use str::{read_numbers, split_commas, split_html_space_chars}; | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ use error_reporting::{ContextualParseError, ParseErrorReporter}; | |||
| use parser::{ParserContext, ParserErrorContext, Parse}; | ||||
| use selectors::parser::SelectorParseErrorKind; | ||||
| use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::borrow::Cow; | ||||
| use std::fmt; | ||||
| use std::ops::Range; | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ use selector_map::{PrecomputedHashSet, PrecomputedHashMap}; | |||
| use selectors::parser::SelectorParseErrorKind; | ||||
| use servo_arc::Arc; | ||||
| use smallvec::SmallVec; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::borrow::{Borrow, Cow}; | ||||
| use std::cmp; | ||||
| use std::fmt; | ||||
|  |  | |||
|  | @ -466,20 +466,6 @@ impl ElementData { | |||
|         self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty() | ||||
|     } | ||||
| 
 | ||||
|     /// If an ancestor is already getting reconstructed by Gecko's top-down
 | ||||
|     /// frame constructor, no need to apply damage.  Similarly if we already
 | ||||
|     /// have an explicitly stored ReconstructFrame hint.
 | ||||
|     ///
 | ||||
|     /// See https://bugzilla.mozilla.org/show_bug.cgi?id=1301258#c12
 | ||||
|     /// for followup work to make the optimization here more optimal by considering
 | ||||
|     /// each bit individually.
 | ||||
|     #[cfg(feature = "gecko")] | ||||
|     pub fn skip_applying_damage(&self) -> bool { self.reconstructed_self_or_ancestor() } | ||||
| 
 | ||||
|     /// N/A in Servo.
 | ||||
|     #[cfg(feature = "servo")] | ||||
|     pub fn skip_applying_damage(&self) -> bool { false } | ||||
| 
 | ||||
|     /// Returns whether it is safe to perform cousin sharing based on the ComputedValues
 | ||||
|     /// identity of the primary style in this ElementData. There are a few subtle things
 | ||||
|     /// to check.
 | ||||
|  |  | |||
|  | @ -1377,7 +1377,7 @@ impl PseudoElement { | |||
|     /// Returns `None` if the pseudo-element is not recognised.
 | ||||
|     #[inline] | ||||
|     pub fn from_slice(s: &str, in_ua_stylesheet: bool) -> Option<Self> { | ||||
|         use std::ascii::AsciiExt; | ||||
|         #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| 
 | ||||
|         // We don't need to support tree pseudos because functional
 | ||||
|         // pseudo-elements needs arguments, and thus should be created
 | ||||
|  | @ -1747,7 +1747,7 @@ impl PseudoElement { | |||
|     /// Returns `None` if the pseudo-element is not recognized.
 | ||||
|     #[inline] | ||||
|     pub fn tree_pseudo_element(name: &str, args: Box<[Atom]>) -> Option<Self> { | ||||
|         use std::ascii::AsciiExt; | ||||
|         #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
|         debug_assert!(name.starts_with("-moz-tree-")); | ||||
|         let tree_part = &name[10..]; | ||||
|             if tree_part.eq_ignore_ascii_case("column") { | ||||
|  |  | |||
|  | @ -225,7 +225,7 @@ impl PseudoElement { | |||
|     /// Returns `None` if the pseudo-element is not recognised.
 | ||||
|     #[inline] | ||||
|     pub fn from_slice(s: &str, in_ua_stylesheet: bool) -> Option<Self> { | ||||
|         use std::ascii::AsciiExt; | ||||
|         #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| 
 | ||||
|         // We don't need to support tree pseudos because functional
 | ||||
|         // pseudo-elements needs arguments, and thus should be created
 | ||||
|  | @ -247,7 +247,7 @@ impl PseudoElement { | |||
|     /// Returns `None` if the pseudo-element is not recognized.
 | ||||
|     #[inline] | ||||
|     pub fn tree_pseudo_element(name: &str, args: Box<[Atom]>) -> Option<Self> { | ||||
|         use std::ascii::AsciiExt; | ||||
|         #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
|         debug_assert!(name.starts_with("-moz-tree-")); | ||||
|         let tree_part = &name[10..]; | ||||
|         % for pseudo in TREE_PSEUDOS: | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ use gecko_bindings::bindings::Gecko_ReleaseAtom; | |||
| use gecko_bindings::structs::{nsAtom, nsAtom_AtomKind, nsStaticAtom}; | ||||
| use nsstring::{nsAString, nsStr}; | ||||
| use precomputed_hash::PrecomputedHash; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::borrow::{Cow, Borrow}; | ||||
| use std::char::{self, DecodeUtf16}; | ||||
| use std::fmt::{self, Write}; | ||||
|  |  | |||
|  | @ -333,7 +333,6 @@ trait PrivateMatchMethods: TElement { | |||
|     fn accumulate_damage_for( | ||||
|         &self, | ||||
|         shared_context: &SharedStyleContext, | ||||
|         skip_applying_damage: bool, | ||||
|         damage: &mut RestyleDamage, | ||||
|         old_values: &ComputedValues, | ||||
|         new_values: &ComputedValues, | ||||
|  | @ -345,9 +344,7 @@ trait PrivateMatchMethods: TElement { | |||
|         let difference = | ||||
|             self.compute_style_difference(old_values, new_values, pseudo); | ||||
| 
 | ||||
|         if !skip_applying_damage { | ||||
|             *damage |= difference.damage; | ||||
|         } | ||||
|         *damage |= difference.damage; | ||||
| 
 | ||||
|         debug!(" > style difference: {:?}", difference); | ||||
| 
 | ||||
|  | @ -590,7 +587,6 @@ pub trait MatchMethods : TElement { | |||
|             cascade_requirement, | ||||
|             self.accumulate_damage_for( | ||||
|                 context.shared, | ||||
|                 data.skip_applying_damage(), | ||||
|                 &mut data.damage, | ||||
|                 &old_primary_style, | ||||
|                 new_primary_style, | ||||
|  | @ -612,7 +608,6 @@ pub trait MatchMethods : TElement { | |||
|                 (&Some(ref old), &Some(ref new)) => { | ||||
|                     self.accumulate_damage_for( | ||||
|                         context.shared, | ||||
|                         data.skip_applying_damage(), | ||||
|                         &mut data.damage, | ||||
|                         old, | ||||
|                         new, | ||||
|  |  | |||
|  | @ -1577,7 +1577,7 @@ https://drafts.csswg.org/css-fonts-4/#low-level-font-variation-settings-control- | |||
| 
 | ||||
|         #[inline] | ||||
|         fn to_computed_value(&self, _context: &Context) -> computed_value::T { | ||||
|             use std::ascii::AsciiExt; | ||||
|             #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
|             match *self { | ||||
|                 SpecifiedValue::Normal => computed_value::T(0), | ||||
|                 SpecifiedValue::Override(ref lang) => { | ||||
|  |  | |||
|  | @ -88,7 +88,7 @@ | |||
|     impl Parse for computed_value::Keyword { | ||||
|         fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) | ||||
|                          -> Result<computed_value::Keyword, ParseError<'i>> { | ||||
|             use std::ascii::AsciiExt; | ||||
|             #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
|             use style_traits::cursor::Cursor; | ||||
|             let location = input.current_source_location(); | ||||
|             let ident = input.expect_ident()?; | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
| #![deny(missing_docs)] | ||||
| 
 | ||||
| use num_traits::ToPrimitive; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::borrow::Cow; | ||||
| use std::convert::AsRef; | ||||
| use std::iter::{Filter, Peekable}; | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ use properties::StyleBuilder; | |||
| use rule_cache::RuleCacheConditions; | ||||
| use selectors::parser::SelectorParseErrorKind; | ||||
| use shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard}; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::borrow::Cow; | ||||
| use std::cell::RefCell; | ||||
| use std::fmt; | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ use Atom; | |||
| pub use cssparser::{RGBA, Token, Parser, serialize_identifier, CowRcStr, SourceLocation}; | ||||
| use parser::{Parse, ParserContext}; | ||||
| use selectors::parser::SelectorParseErrorKind; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::fmt::{self, Debug}; | ||||
| use std::hash; | ||||
| use style_traits::{ToCss, ParseError, StyleParseErrorKind}; | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ use cssparser::Parser; | |||
| use gecko_bindings::structs; | ||||
| use parser::{Parse, ParserContext}; | ||||
| use selectors::parser::SelectorParseErrorKind; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::fmt; | ||||
| use style_traits::{ToCss, ParseError, StyleParseErrorKind}; | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| 
 | ||||
| use cssparser::{Parser, Token}; | ||||
| use parser::{ParserContext, Parse}; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::fmt; | ||||
| use style_traits::{ToCss, ParseError}; | ||||
| use values::CSSFloat; | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ | |||
| 
 | ||||
| use cssparser::{Parser, Token}; | ||||
| use parser::ParserContext; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::fmt; | ||||
| use style_traits::{ToCss, ParseError, StyleParseErrorKind}; | ||||
| use style_traits::values::specified::AllowedNumericType; | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
| 
 | ||||
| use cssparser::{Parser, Token, ParseError as CssParseError}; | ||||
| use parser::{Parse, ParserContext}; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::mem; | ||||
| use style_traits::{ParseError, StyleParseErrorKind}; | ||||
| use values::{CSSFloat, CustomIdent}; | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ use euclid::Size2D; | |||
| use font_metrics::FontMetricsQueryResult; | ||||
| use parser::{Parse, ParserContext}; | ||||
| use std::{cmp, fmt, mem}; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::ops::{Add, Mul}; | ||||
| use style_traits::{ToCss, ParseError, StyleParseErrorKind}; | ||||
| use style_traits::values::specified::AllowedNumericType; | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ use context::QuirksMode; | |||
| use cssparser::{Parser, Token, serialize_identifier}; | ||||
| use parser::{ParserContext, Parse}; | ||||
| use self::url::SpecifiedUrl; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::f32; | ||||
| use std::fmt; | ||||
| use style_traits::{ToCss, ParseError, StyleParseErrorKind}; | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| 
 | ||||
| use cssparser::{Parser, Token}; | ||||
| use parser::{Parse, ParserContext}; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::fmt; | ||||
| use style_traits::{ParseError, ToCss}; | ||||
| use style_traits::values::specified::AllowedNumericType; | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
| use cssparser::{Parser, Token}; | ||||
| use parser::{Parse, ParserContext}; | ||||
| use selectors::parser::SelectorParseErrorKind; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use style_traits::ParseError; | ||||
| use values::computed::{Context, ToComputedValue}; | ||||
| use values::computed::text::LineHeight as ComputedLineHeight; | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| 
 | ||||
| use cssparser::{Parser, Token}; | ||||
| use parser::{ParserContext, Parse}; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::fmt; | ||||
| use style_traits::{ToCss, ParseError, StyleParseErrorKind}; | ||||
| use style_traits::values::specified::AllowedNumericType; | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
| use {CSSPixel, PinchZoomFactor, ParseError, ToCss}; | ||||
| use cssparser::Parser; | ||||
| use euclid::TypedSize2D; | ||||
| use std::ascii::AsciiExt; | ||||
| #[allow(unused_imports)] use std::ascii::AsciiExt; | ||||
| use std::fmt; | ||||
| 
 | ||||
| define_css_keyword_enum!(UserZoom: | ||||
|  |  | |||
|  | @ -265,7 +265,6 @@ class MachCommands(CommandBase): | |||
|             shutil.copy(path.join(self.android_support_dir(), "openssl.sh"), openssl_dir) | ||||
| 
 | ||||
|             # Check if the NDK version is 12 | ||||
|             env["ANDROID_NDK_ROOT"] = env["ANDROID_NDK"] | ||||
|             with open(path.join(env["ANDROID_NDK"], 'source.properties')) as ndk_properties: | ||||
|                 lines = ndk_properties.readlines() | ||||
|                 if lines[1].split(' = ')[1].split('.')[0] != '12': | ||||
|  |  | |||
|  | @ -231,26 +231,6 @@ Service::getSingleton() | |||
|   return service.forget(); | ||||
| } | ||||
| 
 | ||||
| nsIXPConnect *Service::sXPConnect = nullptr; | ||||
| 
 | ||||
| // static
 | ||||
| already_AddRefed<nsIXPConnect> | ||||
| Service::getXPConnect() | ||||
| { | ||||
|   NS_PRECONDITION(NS_IsMainThread(), | ||||
|                   "Must only get XPConnect on the main thread!"); | ||||
|   NS_PRECONDITION(gService, | ||||
|                   "Can not get XPConnect without an instance of our service!"); | ||||
| 
 | ||||
|   // If we've been shutdown, sXPConnect will be null.  To prevent leaks, we do
 | ||||
|   // not cache the service after this point.
 | ||||
|   nsCOMPtr<nsIXPConnect> xpc(sXPConnect); | ||||
|   if (!xpc) | ||||
|     xpc = do_GetService(nsIXPConnect::GetCID()); | ||||
|   NS_ASSERTION(xpc, "Could not get XPConnect!"); | ||||
|   return xpc.forget(); | ||||
| } | ||||
| 
 | ||||
| int32_t Service::sSynchronousPref; | ||||
| 
 | ||||
| // static
 | ||||
|  | @ -279,8 +259,6 @@ Service::~Service() | |||
|   if (rc != SQLITE_OK) | ||||
|     NS_WARNING("Failed to unregister sqlite vfs wrapper."); | ||||
| 
 | ||||
|   shutdown(); // To release sXPConnect.
 | ||||
| 
 | ||||
|   gService = nullptr; | ||||
|   delete mSqliteVFS; | ||||
|   mSqliteVFS = nullptr; | ||||
|  | @ -395,18 +373,11 @@ Service::minimizeMemory() | |||
|   } | ||||
| } | ||||
| 
 | ||||
| void | ||||
| Service::shutdown() | ||||
| { | ||||
|   NS_IF_RELEASE(sXPConnect); | ||||
| } | ||||
| 
 | ||||
| sqlite3_vfs *ConstructTelemetryVFS(); | ||||
| const char *GetVFSName(); | ||||
| 
 | ||||
| static const char* sObserverTopics[] = { | ||||
|   "memory-pressure", | ||||
|   "xpcom-shutdown", | ||||
|   "xpcom-shutdown-threads" | ||||
| }; | ||||
| 
 | ||||
|  | @ -428,8 +399,6 @@ Service::initialize() | |||
|     NS_WARNING("Failed to register telemetry VFS"); | ||||
|   } | ||||
| 
 | ||||
|   // Register for xpcom-shutdown so we can cleanup after ourselves.  The
 | ||||
|   // observer service can only be used on the main thread.
 | ||||
|   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); | ||||
|   NS_ENSURE_TRUE(os, NS_ERROR_FAILURE); | ||||
| 
 | ||||
|  | @ -440,10 +409,6 @@ Service::initialize() | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // We cache XPConnect for our language helpers.  XPConnect can only be
 | ||||
|   // used on the main thread.
 | ||||
|   (void)CallGetService(nsIXPConnect::GetCID(), &sXPConnect); | ||||
| 
 | ||||
|   // We need to obtain the toolkit.storage.synchronous preferences on the main
 | ||||
|   // thread because the preference service can only be accessed there.  This
 | ||||
|   // is cached in the service for all future Open[Unshared]Database calls.
 | ||||
|  | @ -804,8 +769,6 @@ Service::Observe(nsISupports *, const char *aTopic, const char16_t *) | |||
| { | ||||
|   if (strcmp(aTopic, "memory-pressure") == 0) { | ||||
|     minimizeMemory(); | ||||
|   } else if (strcmp(aTopic, "xpcom-shutdown") == 0) { | ||||
|     shutdown(); | ||||
|   } else if (strcmp(aTopic, "xpcom-shutdown-threads") == 0) { | ||||
|     // The Service is kept alive by our strong observer references and
 | ||||
|     // references held by Connection instances.  Since we're about to remove the
 | ||||
|  |  | |||
|  | @ -18,7 +18,6 @@ | |||
| #include "mozIStorageService.h" | ||||
| 
 | ||||
| class nsIMemoryReporter; | ||||
| class nsIXPConnect; | ||||
| struct sqlite3_vfs; | ||||
| 
 | ||||
| namespace mozilla { | ||||
|  | @ -59,12 +58,6 @@ public: | |||
|   NS_DECL_NSIOBSERVER | ||||
|   NS_DECL_NSIMEMORYREPORTER | ||||
| 
 | ||||
|   /**
 | ||||
|    * Obtains an already AddRefed pointer to XPConnect.  This is used by | ||||
|    * language helpers. | ||||
|    */ | ||||
|   static already_AddRefed<nsIXPConnect> getXPConnect(); | ||||
| 
 | ||||
|   /**
 | ||||
|    * Obtains the cached data for the toolkit.storage.synchronous preference. | ||||
|    */ | ||||
|  | @ -156,11 +149,6 @@ private: | |||
|    */ | ||||
|   void minimizeMemory(); | ||||
| 
 | ||||
|   /**
 | ||||
|    * Shuts down the storage service, freeing all of the acquired resources. | ||||
|    */ | ||||
|   void shutdown(); | ||||
| 
 | ||||
|   /**
 | ||||
|    * Lazily creates and returns a collation created from the application's | ||||
|    * locale that all statements of all Connections of this Service may use. | ||||
|  | @ -185,8 +173,6 @@ private: | |||
| 
 | ||||
|   static Service *gService; | ||||
| 
 | ||||
|   static nsIXPConnect *sXPConnect; | ||||
| 
 | ||||
|   static int32_t sSynchronousPref; | ||||
|   static int32_t sDefaultPageSize; | ||||
| }; | ||||
|  |  | |||
|  | @ -21,6 +21,8 @@ | |||
| 
 | ||||
| #include "xpc_make_class.h" | ||||
| 
 | ||||
| #include "mozilla/Services.h" | ||||
| 
 | ||||
| namespace mozilla { | ||||
| namespace storage { | ||||
| 
 | ||||
|  | @ -33,7 +35,7 @@ stepFunc(JSContext *aCtx, | |||
|          uint32_t, | ||||
|          JS::Value *_vp) | ||||
| { | ||||
|   nsCOMPtr<nsIXPConnect> xpc(Service::getXPConnect()); | ||||
|   nsCOMPtr<nsIXPConnect> xpc(mozilla::services::GetXPConnect()); | ||||
|   nsCOMPtr<nsIXPConnectWrappedNative> wrapper; | ||||
|   JSObject *obj = JS_THIS_OBJECT(aCtx, _vp); | ||||
|   if (!obj) { | ||||
|  |  | |||
|  | @ -384307,6 +384307,12 @@ | |||
|      {} | ||||
|     ] | ||||
|    ], | ||||
|    "webdriver/tests/actions/key_shortcuts.py": [ | ||||
|     [ | ||||
|      "/webdriver/tests/actions/key_shortcuts.py", | ||||
|      {} | ||||
|     ] | ||||
|    ], | ||||
|    "webdriver/tests/actions/modifier_click.py": [ | ||||
|     [ | ||||
|      "/webdriver/tests/actions/modifier_click.py", | ||||
|  | @ -541911,7 +541917,7 @@ | |||
|    "testharness" | ||||
|   ], | ||||
|   "html/browsers/origin/cross-origin-objects/cross-origin-objects.html": [ | ||||
|    "9202ebf4d640ffccec49451bae23526c24a1053b", | ||||
|    "ce27e8e729f434ce3e908a49a1ffd733bcdcd06a", | ||||
|    "testharness" | ||||
|   ], | ||||
|   "html/browsers/origin/cross-origin-objects/frame.html": [ | ||||
|  | @ -586530,6 +586536,10 @@ | |||
|    "69542dc107d881bf18dfff3203bfd7a9ec31b4ad", | ||||
|    "wdspec" | ||||
|   ], | ||||
|   "webdriver/tests/actions/key_shortcuts.py": [ | ||||
|    "dbe27dd0b1625169fc8cc2055f8fb49d5a4a78d2", | ||||
|    "wdspec" | ||||
|   ], | ||||
|   "webdriver/tests/actions/modifier_click.py": [ | ||||
|    "2ec22f44973e6da3b9506ad7cc9fd0949f3ef8b5", | ||||
|    "wdspec" | ||||
|  | @ -586551,7 +586561,7 @@ | |||
|    "support" | ||||
|   ], | ||||
|   "webdriver/tests/actions/support/keys.py": [ | ||||
|    "636991372c21e52b623ed4ada9dfb675dd7f7e14", | ||||
|    "61fc98ac2abeeb82486e6689c9cc16d0aa444b69", | ||||
|    "support" | ||||
|   ], | ||||
|   "webdriver/tests/actions/support/refine.py": [ | ||||
|  | @ -596623,7 +596633,7 @@ | |||
|    "testharness" | ||||
|   ], | ||||
|   "webrtc/RTCDTMFSender-helper.js": [ | ||||
|    "54456b1c74d55552fdad0405f55dcd728205b561", | ||||
|    "0c2e8862deffeec71ac925642647bb9ee4ad70ff", | ||||
|    "support" | ||||
|   ], | ||||
|   "webrtc/RTCDTMFSender-insertDTMF.https.html": [ | ||||
|  |  | |||
|  | @ -0,0 +1,49 @@ | |||
| from tests.actions.support.keys import Keys, MODIFIER_KEY | ||||
| from tests.actions.support.refine import get_keys | ||||
| 
 | ||||
| 
 | ||||
| def test_mod_a_and_backspace_deletes_all_text(session, key_reporter, key_chain): | ||||
|     key_chain.send_keys("abc d") \ | ||||
|              .key_down(MODIFIER_KEY) \ | ||||
|              .key_down("a") \ | ||||
|              .key_up(MODIFIER_KEY) \ | ||||
|              .key_up("a") \ | ||||
|              .key_down(Keys.BACKSPACE) \ | ||||
|              .perform() | ||||
|     assert get_keys(key_reporter) == "" | ||||
| 
 | ||||
| 
 | ||||
| def test_mod_a_mod_c_right_mod_v_pastes_text(session, key_reporter, key_chain): | ||||
|     initial = "abc d" | ||||
|     key_chain.send_keys(initial) \ | ||||
|              .key_down(MODIFIER_KEY) \ | ||||
|              .key_down("a") \ | ||||
|              .key_up(MODIFIER_KEY) \ | ||||
|              .key_up("a") \ | ||||
|              .key_down(MODIFIER_KEY) \ | ||||
|              .key_down("c") \ | ||||
|              .key_up(MODIFIER_KEY) \ | ||||
|              .key_up("c") \ | ||||
|              .send_keys([Keys.RIGHT]) \ | ||||
|              .key_down(MODIFIER_KEY) \ | ||||
|              .key_down("v") \ | ||||
|              .key_up(MODIFIER_KEY) \ | ||||
|              .key_up("v") \ | ||||
|              .perform() | ||||
|     assert get_keys(key_reporter) == initial * 2 | ||||
| 
 | ||||
| 
 | ||||
| def test_mod_a_mod_x_deletes_all_text(session, key_reporter, key_chain): | ||||
|     key_chain.send_keys("abc d") \ | ||||
|              .key_down(MODIFIER_KEY) \ | ||||
|              .key_down("a") \ | ||||
|              .key_up(MODIFIER_KEY) \ | ||||
|              .key_up("a") \ | ||||
|              .key_down(MODIFIER_KEY) \ | ||||
|              .key_down("x") \ | ||||
|              .key_up(MODIFIER_KEY) \ | ||||
|              .key_up("x") \ | ||||
|              .perform() | ||||
|     assert get_keys(key_reporter) == "" | ||||
| 
 | ||||
| 
 | ||||
|  | @ -20,6 +20,7 @@ The Keys implementation. | |||
| """ | ||||
| 
 | ||||
| from inspect import getmembers | ||||
| import sys | ||||
| 
 | ||||
| 
 | ||||
| class Keys(object): | ||||
|  | @ -740,3 +741,8 @@ ALL_EVENTS = { | |||
|         "value": u"\ue040", | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| if sys.platform == 'darwin': | ||||
|     MODIFIER_KEY = Keys.META | ||||
| else: | ||||
|     MODIFIER_KEY = Keys.CONTROL | ||||
|  |  | |||
|  | @ -82,7 +82,7 @@ const FRECENCY_DEFAULT = 1000; | |||
| const MAXIMUM_ALLOWED_EXTENSION_MATCHES = 6; | ||||
| 
 | ||||
| // After this time, we'll give up waiting for the extension to return matches.
 | ||||
| const MAXIMUM_ALLOWED_EXTENSION_TIME_MS = 5000; | ||||
| const MAXIMUM_ALLOWED_EXTENSION_TIME_MS = 3000; | ||||
| 
 | ||||
| // A regex that matches "single word" hostnames for whitelisting purposes.
 | ||||
| // The hostname will already have been checked for general validity, so we
 | ||||
|  | @ -95,6 +95,9 @@ const REGEXP_USER_CONTEXT_ID = /(?:^| )user-context-id:(\d+)/; | |||
| // Regex used to match one or more whitespace.
 | ||||
| const REGEXP_SPACES = /\s+/; | ||||
| 
 | ||||
| // The result is notified on a delay, to avoid rebuilding the panel at every match.
 | ||||
| const NOTIFYRESULT_DELAY_MS = 16; | ||||
| 
 | ||||
| // Sqlite result row index constants.
 | ||||
| const QUERYINDEX_QUERYTYPE     = 0; | ||||
| const QUERYINDEX_URL           = 1; | ||||
|  | @ -330,6 +333,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "textURIService", | |||
| function setTimeout(callback, ms) { | ||||
|   let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); | ||||
|   timer.initWithCallback(callback, ms, timer.TYPE_ONE_SHOT); | ||||
|   return timer; | ||||
| } | ||||
| 
 | ||||
| function convertBucketsCharPrefToArray(str) { | ||||
|  | @ -992,6 +996,9 @@ Search.prototype = { | |||
|     // Avoid multiple calls or re-entrance.
 | ||||
|     if (!this.pending) | ||||
|       return; | ||||
|     if (this._notifyTimer) | ||||
|       this._notifyTimer.cancel(); | ||||
|     this._notifyDelaysCount = 0; | ||||
|     if (this._sleepTimer) | ||||
|       this._sleepTimer.cancel(); | ||||
|     if (this._sleepResolve) { | ||||
|  | @ -1127,7 +1134,6 @@ Search.prototype = { | |||
|           // We're done if we're restricting to search suggestions.
 | ||||
|           // Notify the result completion then stop the search.
 | ||||
|           this._autocompleteSearch.finishSearch(true); | ||||
|           this.stop(); | ||||
|           return; | ||||
|         } | ||||
|       } | ||||
|  | @ -1667,9 +1673,8 @@ Search.prototype = { | |||
| 
 | ||||
|     // Since the extension has no way to signale when it's done pushing
 | ||||
|     // results, we add a timeout racing with the addition.
 | ||||
|     let timeoutPromise = new Promise((resolve, reject) => { | ||||
|       setTimeout(() => reject(new Error("timeout waiting for the extension to add its results to the location bar")), | ||||
|                  MAXIMUM_ALLOWED_EXTENSION_TIME_MS); | ||||
|     let timeoutPromise = new Promise(resolve => { | ||||
|       setTimeout(resolve, MAXIMUM_ALLOWED_EXTENSION_TIME_MS); | ||||
|     }); | ||||
|     return Promise.race([timeoutPromise, promise]).catch(Cu.reportError); | ||||
|   }, | ||||
|  | @ -1909,7 +1914,7 @@ Search.prototype = { | |||
|       TelemetryStopwatch.finish(TELEMETRY_1ST_RESULT, this); | ||||
|     if (this._currentMatchCount == 6) | ||||
|       TelemetryStopwatch.finish(TELEMETRY_6_FIRST_RESULTS, this); | ||||
|     this.notifyResults(true); | ||||
|     this.notifyResult(true, match.type == MATCHTYPE.HEURISTIC); | ||||
|   }, | ||||
| 
 | ||||
|   _getInsertIndexForMatch(match) { | ||||
|  | @ -2009,7 +2014,7 @@ Search.prototype = { | |||
|       } | ||||
|     } | ||||
|     if (changed && notify) { | ||||
|       this.notifyResults(true); | ||||
|       this.notifyResult(true); | ||||
|     } | ||||
|   }, | ||||
| 
 | ||||
|  | @ -2364,24 +2369,47 @@ Search.prototype = { | |||
|     return query; | ||||
|   }, | ||||
| 
 | ||||
|  /** | ||||
|    * Notifies the listener about results. | ||||
|   // The result is notified to the search listener on a timer, to chunk multiple
 | ||||
|   // match updates together and avoid rebuilding the popup at every new match.
 | ||||
|   _notifyTimer: null, | ||||
| 
 | ||||
|   /** | ||||
|    * Notifies the current result to the listener. | ||||
|    * | ||||
|    * @param searchOngoing | ||||
|    *        Indicates whether the search is ongoing. | ||||
|    *        Indicates whether the search result should be marked as ongoing. | ||||
|    * @param skipDelay | ||||
|    *        Whether to notify immediately. | ||||
|    */ | ||||
|   notifyResults(searchOngoing) { | ||||
|     let result = this._result; | ||||
|     let resultCode = this._currentMatchCount ? "RESULT_SUCCESS" : "RESULT_NOMATCH"; | ||||
|     if (searchOngoing) { | ||||
|       resultCode += "_ONGOING"; | ||||
|   _notifyDelaysCount: 0, | ||||
|   notifyResult(searchOngoing, skipDelay = false) { | ||||
|     let notify = () => { | ||||
|       this._notifyDelaysCount = 0; | ||||
|       let resultCode = this._currentMatchCount ? "RESULT_SUCCESS" : "RESULT_NOMATCH"; | ||||
|       if (searchOngoing) { | ||||
|         resultCode += "_ONGOING"; | ||||
|       } | ||||
|       let result = this._result; | ||||
|       result.setSearchResult(Ci.nsIAutoCompleteResult[resultCode]); | ||||
|       this._listener.onSearchResult(this._autocompleteSearch, result); | ||||
|       if (!searchOngoing) { | ||||
|         // Break possible cycles.
 | ||||
|         this._listener = null; | ||||
|         this._autocompleteSearch = null; | ||||
|         this.stop(); | ||||
|       } | ||||
|     }; | ||||
|     if (this._notifyTimer) { | ||||
|       this._notifyTimer.cancel(); | ||||
|     } | ||||
|     result.setSearchResult(Ci.nsIAutoCompleteResult[resultCode]); | ||||
|     this._listener.onSearchResult(this._autocompleteSearch, result); | ||||
|     if (!searchOngoing) { | ||||
|       // Break possible cycles.
 | ||||
|       this._listener = null; | ||||
|       this._autocompleteSearch = null; | ||||
|     // In the worst case, we may get evenly spaced matches that would end up
 | ||||
|     // delaying the UI by N_MATCHES * NOTIFYRESULT_DELAY_MS. Thus, we clamp the
 | ||||
|     // number of times we may delay matches.
 | ||||
|     if (skipDelay || this._notifyDelaysCount > 3) { | ||||
|       notify(); | ||||
|     } else { | ||||
|       this._notifyDelaysCount++; | ||||
|       this._notifyTimer = setTimeout(notify, NOTIFYRESULT_DELAY_MS); | ||||
|     } | ||||
|   }, | ||||
| }; | ||||
|  | @ -2579,7 +2607,6 @@ UnifiedComplete.prototype = { | |||
|     if (!notify || !search.pending) | ||||
|       return; | ||||
| 
 | ||||
| 
 | ||||
|     // If we are in restrict mode and we reused the previous search results,
 | ||||
|     // it's possible we didn't go through all the cleanup methods due to early
 | ||||
|     // bailouts. Thus we could still have nonmatching results to remove.
 | ||||
|  | @ -2591,10 +2618,10 @@ UnifiedComplete.prototype = { | |||
|     // onSearchComplete.
 | ||||
|     // If onSearchComplete immediately starts a new search it will set a new
 | ||||
|     // _currentSearch, and on return the execution will continue here, after
 | ||||
|     // notifyResults.
 | ||||
|     // Thus, ensure that notifyResults is the last call in this method,
 | ||||
|     // notifyResult.
 | ||||
|     // Thus, ensure that notifyResult is the last call in this method,
 | ||||
|     // otherwise you might be touching the wrong search.
 | ||||
|     search.notifyResults(false); | ||||
|     search.notifyResult(false); | ||||
|   }, | ||||
| 
 | ||||
|   // nsIAutoCompleteSearchDescriptor
 | ||||
|  |  | |||
|  | @ -5,13 +5,8 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | ||||
| 
 | ||||
| 
 | ||||
| var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. | ||||
|          getService(Ci.nsINavBookmarksService); | ||||
| var hs = Cc["@mozilla.org/browser/nav-history-service;1"]. | ||||
|          getService(Ci.nsINavHistoryService); | ||||
| 
 | ||||
| function check_queries_results(aQueries, aOptions, aExpectedItemIds) { | ||||
|   var result = hs.executeQueries(aQueries, aQueries.length, aOptions); | ||||
| function check_queries_results(aQueries, aOptions, aExpectedBookmarks) { | ||||
|   var result = PlacesUtils.history.executeQueries(aQueries, aQueries.length, aOptions); | ||||
|   var root = result.root; | ||||
|   root.containerOpen = true; | ||||
| 
 | ||||
|  | @ -20,50 +15,62 @@ function check_queries_results(aQueries, aOptions, aExpectedItemIds) { | |||
|     dump("nodes[" + i + "]: " + root.getChild(0).title + "\n"); | ||||
|   } | ||||
| 
 | ||||
|   do_check_eq(root.childCount, aExpectedItemIds.length); | ||||
|   do_check_eq(root.childCount, aExpectedBookmarks.length); | ||||
|   for (let i = 0; i < root.childCount; i++) { | ||||
|     do_check_eq(root.getChild(i).itemId, aExpectedItemIds[i]); | ||||
|     do_check_eq(root.getChild(i).bookmarkGuid, aExpectedBookmarks[i].guid); | ||||
|   } | ||||
| 
 | ||||
|   root.containerOpen = false; | ||||
| } | ||||
| 
 | ||||
| // main
 | ||||
| function run_test() { | ||||
|   var id1 = bs.insertBookmark(bs.bookmarksMenuFolder, uri("http://foo.tld"), | ||||
|                               bs.DEFAULT_INDEX, "123 0"); | ||||
|   var id2 = bs.insertBookmark(bs.bookmarksMenuFolder, uri("http://foo.tld"), | ||||
|                               bs.DEFAULT_INDEX, "456"); | ||||
|   var id3 = bs.insertBookmark(bs.bookmarksMenuFolder, uri("http://foo.tld"), | ||||
|                               bs.DEFAULT_INDEX, "123 456"); | ||||
|   var id4 = bs.insertBookmark(bs.bookmarksMenuFolder, uri("http://foo.tld"), | ||||
|                               bs.DEFAULT_INDEX, "789 456"); | ||||
| add_task(async function run_test() { | ||||
|   let bookmarks = await PlacesUtils.bookmarks.insertTree({ | ||||
|     guid: PlacesUtils.bookmarks.menuGuid, | ||||
|     children: [{ | ||||
|       title: "123 0", | ||||
|       url: "http://foo.tld", | ||||
|     }, { | ||||
|       title: "456", | ||||
|       url: "http://foo.tld", | ||||
|     }, { | ||||
|       title: "123 456", | ||||
|       url: "http://foo.tld", | ||||
|     }, { | ||||
|       title: "789 456", | ||||
|       url: "http://foo.tld", | ||||
|     }] | ||||
|   }); | ||||
| 
 | ||||
|   /** | ||||
|    * All of the query objects are ORed together. Within a query, all the terms | ||||
|    * are ANDed together. See nsINavHistory.idl. | ||||
|    */ | ||||
|   var queries = []; | ||||
|   queries.push(hs.getNewQuery()); | ||||
|   queries.push(hs.getNewQuery()); | ||||
|   var options = hs.getNewQueryOptions(); | ||||
|   queries.push(PlacesUtils.history.getNewQuery()); | ||||
|   queries.push(PlacesUtils.history.getNewQuery()); | ||||
|   var options = PlacesUtils.history.getNewQueryOptions(); | ||||
|   options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS; | ||||
| 
 | ||||
|   // Test 1
 | ||||
|   dump("Test searching for 123 OR 789\n"); | ||||
|   queries[0].searchTerms = "123"; | ||||
|   queries[1].searchTerms = "789"; | ||||
|   check_queries_results(queries, options, [id1, id3, id4]); | ||||
|   check_queries_results(queries, options, [ | ||||
|     bookmarks[0], | ||||
|     bookmarks[2], | ||||
|     bookmarks[3] | ||||
|   ]); | ||||
| 
 | ||||
|   // Test 2
 | ||||
|   dump("Test searching for 123 OR 456\n"); | ||||
|   queries[0].searchTerms = "123"; | ||||
|   queries[1].searchTerms = "456"; | ||||
|   check_queries_results(queries, options, [id1, id2, id3, id4]); | ||||
|   check_queries_results(queries, options, bookmarks); | ||||
| 
 | ||||
|   // Test 3
 | ||||
|   dump("Test searching for 00 OR 789\n"); | ||||
|   queries[0].searchTerms = "00"; | ||||
|   queries[1].searchTerms = "789"; | ||||
|   check_queries_results(queries, options, [id4]); | ||||
| } | ||||
|   check_queries_results(queries, options, [bookmarks[3]]); | ||||
| }); | ||||
|  |  | |||
|  | @ -4,8 +4,6 @@ | |||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | ||||
| 
 | ||||
| var tests = []; | ||||
| 
 | ||||
| // Get database connection
 | ||||
| try { | ||||
|   var mDBConn = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase) | ||||
|  | @ -19,79 +17,64 @@ try { | |||
|     - don't try to add invalid uri nodes to a JSON backup | ||||
| */ | ||||
| 
 | ||||
| var invalidURITest = { | ||||
|   _itemTitle: "invalid uri", | ||||
|   _itemUrl: "http://test.mozilla.org/", | ||||
|   _itemId: null, | ||||
| const ITEM_TITLE = "invalid uri"; | ||||
| const ITEM_URL = "http://test.mozilla.org"; | ||||
| 
 | ||||
|   populate() { | ||||
|     // add a valid bookmark
 | ||||
|     PlacesUtils.bookmarks.insertBookmark(PlacesUtils.toolbarFolderId, | ||||
|                                          PlacesUtils._uri(this._itemUrl), | ||||
|                                          PlacesUtils.bookmarks.DEFAULT_INDEX, | ||||
|                                          this._itemTitle); | ||||
|     // this bookmark will go corrupt
 | ||||
|     this._itemId = | ||||
|       PlacesUtils.bookmarks.insertBookmark(PlacesUtils.toolbarFolderId, | ||||
|                                            PlacesUtils._uri(this._itemUrl), | ||||
|                                            PlacesUtils.bookmarks.DEFAULT_INDEX, | ||||
|                                            this._itemTitle); | ||||
|   }, | ||||
| function validateResults(expectedValidItemsCount) { | ||||
|   var query = PlacesUtils.history.getNewQuery(); | ||||
|   query.setFolders([PlacesUtils.bookmarks.toolbarFolder], 1); | ||||
|   var options = PlacesUtils.history.getNewQueryOptions(); | ||||
|   var result = PlacesUtils.history.executeQuery(query, options); | ||||
| 
 | ||||
|   clean() { | ||||
|     PlacesUtils.bookmarks.removeItem(this._itemId); | ||||
|   }, | ||||
|   var toolbar = result.root; | ||||
|   toolbar.containerOpen = true; | ||||
| 
 | ||||
|   validate(aExpectValidItemsCount) { | ||||
|     var query = PlacesUtils.history.getNewQuery(); | ||||
|     query.setFolders([PlacesUtils.bookmarks.toolbarFolder], 1); | ||||
|     var options = PlacesUtils.history.getNewQueryOptions(); | ||||
|     var result = PlacesUtils.history.executeQuery(query, options); | ||||
| 
 | ||||
|     var toolbar = result.root; | ||||
|     toolbar.containerOpen = true; | ||||
| 
 | ||||
|     // test for our bookmark
 | ||||
|     do_check_eq(toolbar.childCount, aExpectValidItemsCount); | ||||
|     for (var i = 0; i < toolbar.childCount; i++) { | ||||
|       var folderNode = toolbar.getChild(0); | ||||
|       do_check_eq(folderNode.type, folderNode.RESULT_TYPE_URI); | ||||
|       do_check_eq(folderNode.title, this._itemTitle); | ||||
|     } | ||||
| 
 | ||||
|     // clean up
 | ||||
|     toolbar.containerOpen = false; | ||||
|   // test for our bookmark
 | ||||
|   do_check_eq(toolbar.childCount, expectedValidItemsCount); | ||||
|   for (var i = 0; i < toolbar.childCount; i++) { | ||||
|     var folderNode = toolbar.getChild(0); | ||||
|     do_check_eq(folderNode.type, folderNode.RESULT_TYPE_URI); | ||||
|     do_check_eq(folderNode.title, ITEM_TITLE); | ||||
|   } | ||||
| }; | ||||
| tests.push(invalidURITest); | ||||
| 
 | ||||
|   // clean up
 | ||||
|   toolbar.containerOpen = false; | ||||
| } | ||||
| 
 | ||||
| add_task(async function() { | ||||
|   // make json file
 | ||||
|   let jsonFile = OS.Path.join(OS.Constants.Path.profileDir, "bookmarks.json"); | ||||
| 
 | ||||
|   // populate db
 | ||||
|   tests.forEach(function(aTest) { | ||||
|     aTest.populate(); | ||||
|     // sanity
 | ||||
|     aTest.validate(2); | ||||
|     // Something in the code went wrong and we finish up losing the place, so
 | ||||
|     // the bookmark uri becomes null.
 | ||||
|     var sql = "UPDATE moz_bookmarks SET fk = 1337 WHERE id = ?1"; | ||||
|     var stmt = mDBConn.createStatement(sql); | ||||
|     stmt.bindByIndex(0, aTest._itemId); | ||||
|     try { | ||||
|       stmt.execute(); | ||||
|     } finally { | ||||
|       stmt.finalize(); | ||||
|     } | ||||
|   // add a valid bookmark
 | ||||
|   await PlacesUtils.bookmarks.insert({ | ||||
|     parentGuid: PlacesUtils.bookmarks.toolbarGuid, | ||||
|     title: ITEM_TITLE, | ||||
|     url: ITEM_URL, | ||||
|   }); | ||||
| 
 | ||||
|   let badBookmark = await PlacesUtils.bookmarks.insert({ | ||||
|     parentGuid: PlacesUtils.bookmarks.toolbarGuid, | ||||
|     title: ITEM_TITLE, | ||||
|     url: ITEM_URL, | ||||
|   }); | ||||
|   // sanity
 | ||||
|   validateResults(2); | ||||
|   // Something in the code went wrong and we finish up losing the place, so
 | ||||
|   // the bookmark uri becomes null.
 | ||||
|   var sql = "UPDATE moz_bookmarks SET fk = 1337 WHERE guid = ?1"; | ||||
|   var stmt = mDBConn.createStatement(sql); | ||||
|   stmt.bindByIndex(0, badBookmark.guid); | ||||
|   try { | ||||
|     stmt.execute(); | ||||
|   } finally { | ||||
|     stmt.finalize(); | ||||
|   } | ||||
| 
 | ||||
|   await BookmarkJSONUtils.exportToFile(jsonFile); | ||||
| 
 | ||||
|   // clean
 | ||||
|   tests.forEach(function(aTest) { | ||||
|     aTest.clean(); | ||||
|   }); | ||||
|   await PlacesUtils.bookmarks.remove(badBookmark); | ||||
| 
 | ||||
|   // restore json file
 | ||||
|   try { | ||||
|  | @ -99,9 +82,7 @@ add_task(async function() { | |||
|   } catch (ex) { do_throw("couldn't import the exported file: " + ex); } | ||||
| 
 | ||||
|   // validate
 | ||||
|   tests.forEach(function(aTest) { | ||||
|     aTest.validate(1); | ||||
|   }); | ||||
|   validateResults(1); | ||||
| 
 | ||||
|   // clean up
 | ||||
|   await OS.File.remove(jsonFile); | ||||
|  |  | |||
|  | @ -4,114 +4,94 @@ | |||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | ||||
| 
 | ||||
| var tests = []; | ||||
| 
 | ||||
| /* | ||||
|   This test is: | ||||
|     - don't block while doing backup and restore if tag containers contain | ||||
|       bogus items (separators, folders) | ||||
| */ | ||||
| 
 | ||||
| var invalidTagChildTest = { | ||||
|   _itemTitle: "invalid uri", | ||||
|   _itemUrl: "http://test.mozilla.org/", | ||||
|   _itemId: -1, | ||||
|   _tag: "testTag", | ||||
|   _tagItemId: -1, | ||||
| const ITEM_TITLE = "invalid uri"; | ||||
| const ITEM_URL = "http://test.mozilla.org/"; | ||||
| const TAG_NAME = "testTag"; | ||||
| 
 | ||||
|   populate() { | ||||
|     // add a valid bookmark
 | ||||
|     this._itemId = PlacesUtils.bookmarks | ||||
|                               .insertBookmark(PlacesUtils.toolbarFolderId, | ||||
|                                               PlacesUtils._uri(this._itemUrl), | ||||
|                                               PlacesUtils.bookmarks.DEFAULT_INDEX, | ||||
|                                               this._itemTitle); | ||||
| function validateResults() { | ||||
|   var query = PlacesUtils.history.getNewQuery(); | ||||
|   query.setFolders([PlacesUtils.bookmarks.toolbarFolder], 1); | ||||
|   var options = PlacesUtils.history.getNewQueryOptions(); | ||||
|   var result = PlacesUtils.history.executeQuery(query, options); | ||||
| 
 | ||||
|     // create a tag
 | ||||
|     PlacesUtils.tagging.tagURI(PlacesUtils._uri(this._itemUrl), [this._tag]); | ||||
|     // get tag folder id
 | ||||
|     var options = PlacesUtils.history.getNewQueryOptions(); | ||||
|     var query = PlacesUtils.history.getNewQuery(); | ||||
|     query.setFolders([PlacesUtils.bookmarks.tagsFolder], 1); | ||||
|     var result = PlacesUtils.history.executeQuery(query, options); | ||||
|     var tagRoot = result.root; | ||||
|     tagRoot.containerOpen = true; | ||||
|     do_check_eq(tagRoot.childCount, 1); | ||||
|     var tagNode = tagRoot.getChild(0) | ||||
|                           .QueryInterface(Ci.nsINavHistoryContainerResultNode); | ||||
|     this._tagItemId = tagNode.itemId; | ||||
|     tagRoot.containerOpen = false; | ||||
|   var toolbar = result.root; | ||||
|   toolbar.containerOpen = true; | ||||
| 
 | ||||
|     // add a separator and a folder inside tag folder
 | ||||
|     PlacesUtils.bookmarks.insertSeparator(this._tagItemId, | ||||
|                                          PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
|     PlacesUtils.bookmarks.createFolder(this._tagItemId, | ||||
|                                        "test folder", | ||||
|                                        PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
| 
 | ||||
|     // add a separator and a folder inside tag root
 | ||||
|     PlacesUtils.bookmarks.insertSeparator(PlacesUtils.bookmarks.tagsFolder, | ||||
|                                           PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
|     PlacesUtils.bookmarks.createFolder(PlacesUtils.bookmarks.tagsFolder, | ||||
|                                        "test tags root folder", | ||||
|                                        PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
|   }, | ||||
| 
 | ||||
|   clean() { | ||||
|     PlacesUtils.tagging.untagURI(PlacesUtils._uri(this._itemUrl), [this._tag]); | ||||
|     PlacesUtils.bookmarks.removeItem(this._itemId); | ||||
|   }, | ||||
| 
 | ||||
|   validate() { | ||||
|     var query = PlacesUtils.history.getNewQuery(); | ||||
|     query.setFolders([PlacesUtils.bookmarks.toolbarFolder], 1); | ||||
|     var options = PlacesUtils.history.getNewQueryOptions(); | ||||
|     var result = PlacesUtils.history.executeQuery(query, options); | ||||
| 
 | ||||
|     var toolbar = result.root; | ||||
|     toolbar.containerOpen = true; | ||||
| 
 | ||||
|     // test for our bookmark
 | ||||
|     do_check_eq(toolbar.childCount, 1); | ||||
|     for (var i = 0; i < toolbar.childCount; i++) { | ||||
|       var folderNode = toolbar.getChild(0); | ||||
|       do_check_eq(folderNode.type, folderNode.RESULT_TYPE_URI); | ||||
|       do_check_eq(folderNode.title, this._itemTitle); | ||||
|     } | ||||
|     toolbar.containerOpen = false; | ||||
| 
 | ||||
|     // test for our tag
 | ||||
|     var tags = PlacesUtils.tagging.getTagsForURI(PlacesUtils._uri(this._itemUrl)); | ||||
|     do_check_eq(tags.length, 1); | ||||
|     do_check_eq(tags[0], this._tag); | ||||
|   // test for our bookmark
 | ||||
|   do_check_eq(toolbar.childCount, 1); | ||||
|   for (var i = 0; i < toolbar.childCount; i++) { | ||||
|     var folderNode = toolbar.getChild(0); | ||||
|     do_check_eq(folderNode.type, folderNode.RESULT_TYPE_URI); | ||||
|     do_check_eq(folderNode.title, ITEM_TITLE); | ||||
|   } | ||||
| }; | ||||
| tests.push(invalidTagChildTest); | ||||
|   toolbar.containerOpen = false; | ||||
| 
 | ||||
|   // test for our tag
 | ||||
|   var tags = PlacesUtils.tagging.getTagsForURI(PlacesUtils._uri(ITEM_URL)); | ||||
|   do_check_eq(tags.length, 1); | ||||
|   do_check_eq(tags[0], TAG_NAME); | ||||
| } | ||||
| 
 | ||||
| add_task(async function() { | ||||
|   let jsonFile = OS.Path.join(OS.Constants.Path.profileDir, "bookmarks.json"); | ||||
| 
 | ||||
|   // populate db
 | ||||
|   tests.forEach(function(aTest) { | ||||
|     aTest.populate(); | ||||
|     // sanity
 | ||||
|     aTest.validate(); | ||||
|   // add a valid bookmark
 | ||||
|   let item = await PlacesUtils.bookmarks.insert({ | ||||
|     parentGuid: PlacesUtils.bookmarks.toolbarGuid, | ||||
|     title: ITEM_TITLE, | ||||
|     url: ITEM_URL, | ||||
|   }); | ||||
| 
 | ||||
|   // create a tag
 | ||||
|   PlacesUtils.tagging.tagURI(PlacesUtils._uri(ITEM_URL), [TAG_NAME]); | ||||
|   // get tag folder id
 | ||||
|   var options = PlacesUtils.history.getNewQueryOptions(); | ||||
|   var query = PlacesUtils.history.getNewQuery(); | ||||
|   query.setFolders([PlacesUtils.bookmarks.tagsFolder], 1); | ||||
|   var result = PlacesUtils.history.executeQuery(query, options); | ||||
|   var tagRoot = result.root; | ||||
|   tagRoot.containerOpen = true; | ||||
|   do_check_eq(tagRoot.childCount, 1); | ||||
|   var tagNode = tagRoot.getChild(0) | ||||
|                         .QueryInterface(Ci.nsINavHistoryContainerResultNode); | ||||
|   let tagItemId = tagNode.itemId; | ||||
|   tagRoot.containerOpen = false; | ||||
| 
 | ||||
|   // Currently these use the old API as the new API doesn't support inserting
 | ||||
|   // invalid items into the tag folder.
 | ||||
| 
 | ||||
|   // add a separator and a folder inside tag folder
 | ||||
|   PlacesUtils.bookmarks.insertSeparator(tagItemId, | ||||
|                                        PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
|   PlacesUtils.bookmarks.createFolder(tagItemId, | ||||
|                                      "test folder", | ||||
|                                      PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
| 
 | ||||
|   // add a separator and a folder inside tag root
 | ||||
|   PlacesUtils.bookmarks.insertSeparator(PlacesUtils.bookmarks.tagsFolder, | ||||
|                                         PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
|   PlacesUtils.bookmarks.createFolder(PlacesUtils.bookmarks.tagsFolder, | ||||
|                                      "test tags root folder", | ||||
|                                      PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
|   // sanity
 | ||||
|   validateResults(); | ||||
| 
 | ||||
|   await BookmarkJSONUtils.exportToFile(jsonFile); | ||||
| 
 | ||||
|   // clean
 | ||||
|   tests.forEach(function(aTest) { | ||||
|     aTest.clean(); | ||||
|   }); | ||||
|   PlacesUtils.tagging.untagURI(PlacesUtils._uri(ITEM_URL), [TAG_NAME]); | ||||
|   await PlacesUtils.bookmarks.remove(item); | ||||
| 
 | ||||
|   // restore json file
 | ||||
|   await BookmarkJSONUtils.importFromFile(jsonFile, true); | ||||
| 
 | ||||
|   // validate
 | ||||
|   tests.forEach(function(aTest) { | ||||
|     aTest.validate(); | ||||
|   }); | ||||
|   validateResults(); | ||||
| 
 | ||||
|   // clean up
 | ||||
|   await OS.File.remove(jsonFile); | ||||
|  |  | |||
|  | @ -34,10 +34,12 @@ add_task(async function() { | |||
|   do_check_eq(matches[3], hash); | ||||
| 
 | ||||
|   // Add a bookmark and create another backup.
 | ||||
|   let bookmarkId = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.bookmarks.bookmarksMenuFolder, | ||||
|                                                         uri("http://foo.com"), | ||||
|                                                         PlacesUtils.bookmarks.DEFAULT_INDEX, | ||||
|                                                         "foo"); | ||||
|   let bookmark = await PlacesUtils.bookmarks.insert({ | ||||
|     parentGuid: PlacesUtils.bookmarks.menuGuid, | ||||
|     title: "foo", | ||||
|     url: "http://foo.com", | ||||
|   }); | ||||
| 
 | ||||
|   // We must enforce a backup since one for today already exists.  The forced
 | ||||
|   // backup will replace the existing one.
 | ||||
|   await PlacesBackups.create(undefined, true); | ||||
|  | @ -50,6 +52,6 @@ add_task(async function() { | |||
|   do_check_neq(matches[3], hash); | ||||
| 
 | ||||
|   // Clean up
 | ||||
|   PlacesUtils.bookmarks.removeItem(bookmarkId); | ||||
|   await PlacesUtils.bookmarks.remove(bookmark); | ||||
|   await PlacesBackups.create(0); | ||||
| }); | ||||
|  |  | |||
|  | @ -30,20 +30,21 @@ add_task(async function compress_bookmark_backups_test() { | |||
|   do_check_eq((await PlacesBackups.getBackupFiles()).length, 1); | ||||
| 
 | ||||
|   // Check if import works from lz4 compressed json
 | ||||
|   let uri = NetUtil.newURI("http://www.mozilla.org/en-US/"); | ||||
|   let bm  = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId, | ||||
|                                                  uri, | ||||
|                                                  PlacesUtils.bookmarks.DEFAULT_INDEX, | ||||
|                                                  "bookmark"); | ||||
|   let url = "http://www.mozilla.org/en-US/" | ||||
|   let bm = await PlacesUtils.bookmarks.insert({ | ||||
|     parentGuid: PlacesUtils.bookmarks.unfiledGuid, | ||||
|     title: "bookmark", | ||||
|     url, | ||||
|   }); | ||||
| 
 | ||||
|   // Force create a compressed backup, Remove the bookmark, the restore the backup
 | ||||
|   await PlacesBackups.create(undefined, true); | ||||
|   let recentBackup = await PlacesBackups.getMostRecentBackup(); | ||||
|   PlacesUtils.bookmarks.removeItem(bm); | ||||
|   await PlacesUtils.bookmarks.remove(bm); | ||||
|   await BookmarkJSONUtils.importFromFile(recentBackup, true); | ||||
|   let root = PlacesUtils.getFolderContents(PlacesUtils.unfiledBookmarksFolderId).root; | ||||
|   let node = root.getChild(0); | ||||
|   do_check_eq(node.uri, uri.spec); | ||||
|   do_check_eq(node.uri, url); | ||||
| 
 | ||||
|   root.containerOpen = false; | ||||
|   PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId); | ||||
|  |  | |||
|  | @ -8,11 +8,11 @@ | |||
|  */ | ||||
| add_task(async function test_saveBookmarksToJSONFile_and_create() { | ||||
|   // Add a bookmark
 | ||||
|   let uri = NetUtil.newURI("http://getfirefox.com/"); | ||||
|   let bookmarkId = | ||||
|     PlacesUtils.bookmarks.insertBookmark( | ||||
|       PlacesUtils.unfiledBookmarksFolderId, uri, | ||||
|       PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Firefox!"); | ||||
|   let bookmark = await PlacesUtils.bookmarks.insert({ | ||||
|     parentGuid: PlacesUtils.bookmarks.unfiledGuid, | ||||
|     title: "Get Firefox!", | ||||
|     url: "http://getfirefox.com/" | ||||
|   }); | ||||
| 
 | ||||
|   // Test saveBookmarksToJSONFile()
 | ||||
|   let backupFile = FileUtils.getFile("TmpD", ["bookmarks.json"]); | ||||
|  | @ -47,5 +47,5 @@ add_task(async function test_saveBookmarksToJSONFile_and_create() { | |||
|   // Cleanup
 | ||||
|   backupFile.remove(false); | ||||
|   await PlacesBackups.create(0); | ||||
|   PlacesUtils.bookmarks.removeItem(bookmarkId); | ||||
|   await PlacesUtils.bookmarks.remove(bookmark); | ||||
| }); | ||||
|  |  | |||
|  | @ -8,22 +8,34 @@ | |||
|  * ancestor in the bookmarks table. | ||||
|  */ | ||||
| add_task(async function() { | ||||
|   let bm = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId, | ||||
|                                                 NetUtil.newURI("http://mozilla.org/"), | ||||
|                                                 PlacesUtils.bookmarks.DEFAULT_INDEX, | ||||
|                                                 "bookmark"); | ||||
|   let f2 = PlacesUtils.bookmarks.createFolder(PlacesUtils.unfiledBookmarksFolderId, "f2", | ||||
|                                               PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
|   PlacesUtils.bookmarks.moveItem(bm, f2, PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
|   let f1 = PlacesUtils.bookmarks.createFolder(PlacesUtils.unfiledBookmarksFolderId, "f1", | ||||
|                                               PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
|   PlacesUtils.bookmarks.moveItem(f2, f1, PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
|   let bms = await PlacesUtils.bookmarks.insertTree({ | ||||
|     guid: PlacesUtils.bookmarks.unfiledGuid, | ||||
|     children: [{ | ||||
|       title: "bookmark", | ||||
|       url: "http://mozilla.org", | ||||
|     }, { | ||||
|       title: "f2", | ||||
|       type: PlacesUtils.bookmarks.TYPE_FOLDER, | ||||
|     }, { | ||||
|       title: "f1", | ||||
|       type: PlacesUtils.bookmarks.TYPE_FOLDER, | ||||
|     }] | ||||
|   }); | ||||
| 
 | ||||
|   let bookmark = bms[0]; | ||||
|   let folder2 = bms[1]; | ||||
|   let folder1 = bms[2]; | ||||
|   bookmark.parentGuid = folder2.guid; | ||||
|   await PlacesUtils.bookmarks.update(bookmark); | ||||
| 
 | ||||
|   folder2.parentGuid = folder1.guid; | ||||
|   await PlacesUtils.bookmarks.update(folder2); | ||||
| 
 | ||||
|   // Create a backup.
 | ||||
|   await PlacesBackups.create(); | ||||
| 
 | ||||
|   // Remove the bookmarks, then restore the backup.
 | ||||
|   PlacesUtils.bookmarks.removeItem(f1); | ||||
|   await PlacesUtils.bookmarks.remove(folder1); | ||||
|   await BookmarkJSONUtils.importFromFile((await PlacesBackups.getMostRecentBackup()), true); | ||||
| 
 | ||||
|   do_print("Checking first level"); | ||||
|  | @ -36,7 +48,7 @@ add_task(async function() { | |||
|   do_check_eq(level2.title, "f2"); | ||||
|   do_print("Checking bookmark"); | ||||
|   PlacesUtils.asContainer(level2).containerOpen = true; | ||||
|   let bookmark = level2.getChild(0); | ||||
|   bookmark = level2.getChild(0); | ||||
|   do_check_eq(bookmark.title, "bookmark"); | ||||
|   level2.containerOpen = false; | ||||
|   level1.containerOpen = false; | ||||
|  |  | |||
|  | @ -6,27 +6,28 @@ | |||
|  * Checks that we don't encodeURI twice when creating bookmarks.html. | ||||
|  */ | ||||
| add_task(async function() { | ||||
|   let uri = NetUtil.newURI("http://bt.ktxp.com/search.php?keyword=%E5%A6%84%E6%83%B3%E5%AD%A6%E7%94%9F%E4%BC%9A"); | ||||
|   let bm = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId, | ||||
|                                                 uri, | ||||
|                                                 PlacesUtils.bookmarks.DEFAULT_INDEX, | ||||
|                                                 "bookmark"); | ||||
|   let url = "http://bt.ktxp.com/search.php?keyword=%E5%A6%84%E6%83%B3%E5%AD%A6%E7%94%9F%E4%BC%9A"; | ||||
|   let bm = await PlacesUtils.bookmarks.insert({ | ||||
|     parentGuid: PlacesUtils.bookmarks.unfiledGuid, | ||||
|     title: "bookmark", | ||||
|     url, | ||||
|   }); | ||||
| 
 | ||||
|   let file =  OS.Path.join(OS.Constants.Path.profileDir, "bookmarks.exported.997030.html"); | ||||
|   let file = OS.Path.join(OS.Constants.Path.profileDir, "bookmarks.exported.997030.html"); | ||||
|   if ((await OS.File.exists(file))) { | ||||
|     await OS.File.remove(file); | ||||
|   } | ||||
|   await BookmarkHTMLUtils.exportToFile(file); | ||||
| 
 | ||||
|   // Remove the bookmarks, then restore the backup.
 | ||||
|   PlacesUtils.bookmarks.removeItem(bm); | ||||
|   await PlacesUtils.bookmarks.remove(bm); | ||||
|   await BookmarkHTMLUtils.importFromFile(file, true); | ||||
| 
 | ||||
|   do_print("Checking first level"); | ||||
|   let root = PlacesUtils.getFolderContents(PlacesUtils.unfiledBookmarksFolderId).root; | ||||
|   let node = root.getChild(0); | ||||
|   do_check_eq(node.uri, uri.spec); | ||||
|   do_check_eq(node.uri, url); | ||||
| 
 | ||||
|   root.containerOpen = false; | ||||
|   PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId); | ||||
|   await PlacesUtils.bookmarks.eraseEverything(); | ||||
| }); | ||||
|  |  | |||
|  | @ -4,35 +4,38 @@ | |||
|  * 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/. */
 | ||||
| 
 | ||||
| // get bookmarks root id
 | ||||
| var root = PlacesUtils.bookmarksMenuFolderId; | ||||
| 
 | ||||
| // a search term that matches a default bookmark
 | ||||
| const searchTerm = "about"; | ||||
| 
 | ||||
| var testRoot; | ||||
| var testRootId; | ||||
| 
 | ||||
| // main
 | ||||
| function run_test() { | ||||
| add_task(async function setup() { | ||||
|   // create a folder to hold all the tests
 | ||||
|   // this makes the tests more tolerant of changes to the default bookmarks set
 | ||||
|   // also, name it using the search term, for testing that containers that match don't show up in query results
 | ||||
|   testRoot = PlacesUtils.bookmarks.createFolder( | ||||
|     root, searchTerm, PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
|   testRoot = await PlacesUtils.bookmarks.insert({ | ||||
|     parentGuid: PlacesUtils.bookmarks.menuGuid, | ||||
|     title: searchTerm, | ||||
|     type: PlacesUtils.bookmarks.TYPE_FOLDER, | ||||
|   }); | ||||
|   testRootId = await PlacesUtils.promiseItemId(testRoot.guid); | ||||
| }); | ||||
| 
 | ||||
|   run_next_test(); | ||||
| } | ||||
| 
 | ||||
| add_test(function test_savedsearches_bookmarks() { | ||||
| add_task(async function test_savedsearches_bookmarks() { | ||||
|   // add a bookmark that matches the search term
 | ||||
|   var bookmarkId = PlacesUtils.bookmarks.insertBookmark( | ||||
|     root, uri("http://foo.com"), PlacesUtils.bookmarks.DEFAULT_INDEX, | ||||
|     searchTerm); | ||||
|   let bookmark = await PlacesUtils.bookmarks.insert({ | ||||
|     parentGuid: PlacesUtils.bookmarks.menuGuid, | ||||
|     title: searchTerm, | ||||
|     url: "http://foo.com", | ||||
|   }); | ||||
| 
 | ||||
|   // create a saved-search that matches a default bookmark
 | ||||
|   var searchId = PlacesUtils.bookmarks.insertBookmark( | ||||
|     testRoot, uri("place:terms=" + searchTerm + "&excludeQueries=1&expandQueries=1&queryType=1"), | ||||
|     PlacesUtils.bookmarks.DEFAULT_INDEX, searchTerm); | ||||
|   let search = await PlacesUtils.bookmarks.insert({ | ||||
|     parentGuid: testRoot.guid, | ||||
|     title: searchTerm, | ||||
|     url: "place:terms=" + searchTerm + "&excludeQueries=1&expandQueries=1&queryType=1", | ||||
|   }); | ||||
| 
 | ||||
|   // query for the test root, expandQueries=0
 | ||||
|   // the query should show up as a regular bookmark
 | ||||
|  | @ -40,7 +43,7 @@ add_test(function test_savedsearches_bookmarks() { | |||
|     let options = PlacesUtils.history.getNewQueryOptions(); | ||||
|     options.expandQueries = 0; | ||||
|     let query = PlacesUtils.history.getNewQuery(); | ||||
|     query.setFolders([testRoot], 1); | ||||
|     query.setFolders([testRootId], 1); | ||||
|     let result = PlacesUtils.history.executeQuery(query, options); | ||||
|     let rootNode = result.root; | ||||
|     rootNode.containerOpen = true; | ||||
|  | @ -66,7 +69,7 @@ add_test(function test_savedsearches_bookmarks() { | |||
|     let options = PlacesUtils.history.getNewQueryOptions(); | ||||
|     options.expandQueries = 1; | ||||
|     let query = PlacesUtils.history.getNewQuery(); | ||||
|     query.setFolders([testRoot], 1); | ||||
|     query.setFolders([testRootId], 1); | ||||
|     let result = PlacesUtils.history.executeQuery(query, options); | ||||
|     let rootNode = result.root; | ||||
|     rootNode.containerOpen = true; | ||||
|  | @ -88,7 +91,7 @@ add_test(function test_savedsearches_bookmarks() { | |||
| 
 | ||||
|       // test that bookmark shows in query results
 | ||||
|       var item = node.getChild(0); | ||||
|       do_check_eq(item.itemId, bookmarkId); | ||||
|       do_check_eq(item.bookmarkGuid, bookmark.guid); | ||||
| 
 | ||||
|       // XXX - FAILING - test live-update of query results - add a bookmark that matches the query
 | ||||
|       // var tmpBmId = PlacesUtils.bookmarks.insertBookmark(
 | ||||
|  | @ -101,13 +104,18 @@ add_test(function test_savedsearches_bookmarks() { | |||
|       // do_check_eq(query.childCount, 1);
 | ||||
| 
 | ||||
|       // test live-update of query results - add a folder that matches the query
 | ||||
|       PlacesUtils.bookmarks.createFolder( | ||||
|         root, searchTerm + "zaa", PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
|       await PlacesUtils.bookmarks.insert({ | ||||
|         parentGuid: PlacesUtils.bookmarks.menuGuid, | ||||
|         title: searchTerm + "zaa", | ||||
|         type: PlacesUtils.bookmarks.TYPE_FOLDER, | ||||
|       }); | ||||
|       do_check_eq(node.childCount, 1); | ||||
|       // test live-update of query results - add a query that matches the query
 | ||||
|       PlacesUtils.bookmarks.insertBookmark( | ||||
|         root, uri("place:terms=foo&excludeQueries=1&expandQueries=1&queryType=1"), | ||||
|         PlacesUtils.bookmarks.DEFAULT_INDEX, searchTerm + "blah"); | ||||
|       await PlacesUtils.bookmarks.insert({ | ||||
|         parentGuid: PlacesUtils.bookmarks.menuGuid, | ||||
|         title: searchTerm + "blah", | ||||
|         url: "place:terms=foo&excludeQueries=1&expandQueries=1&queryType=1", | ||||
|       }); | ||||
|       do_check_eq(node.childCount, 1); | ||||
|     } | ||||
|     rootNode.containerOpen = false; | ||||
|  | @ -116,9 +124,7 @@ add_test(function test_savedsearches_bookmarks() { | |||
|   } | ||||
| 
 | ||||
|   // delete the bookmark search
 | ||||
|   PlacesUtils.bookmarks.removeItem(searchId); | ||||
| 
 | ||||
|   run_next_test(); | ||||
|   await PlacesUtils.bookmarks.remove(search); | ||||
| }); | ||||
| 
 | ||||
| add_task(async function test_savedsearches_history() { | ||||
|  | @ -127,9 +133,11 @@ add_task(async function test_savedsearches_history() { | |||
|   await PlacesTestUtils.addVisits({ uri: testURI, title: searchTerm }); | ||||
| 
 | ||||
|   // create a saved-search that matches the visit we added
 | ||||
|   var searchId = PlacesUtils.bookmarks.insertBookmark(testRoot, | ||||
|     uri("place:terms=" + searchTerm + "&excludeQueries=1&expandQueries=1&queryType=0"), | ||||
|     PlacesUtils.bookmarks.DEFAULT_INDEX, searchTerm); | ||||
|   var searchItem = await PlacesUtils.bookmarks.insert({ | ||||
|     parentGuid: testRoot.guid, | ||||
|     title: searchTerm, | ||||
|     url: "place:terms=" + searchTerm + "&excludeQueries=1&expandQueries=1&queryType=0", | ||||
|   }); | ||||
| 
 | ||||
|   // query for the test root, expandQueries=1
 | ||||
|   // the query should show up as a query container, with 1 child
 | ||||
|  | @ -137,7 +145,7 @@ add_task(async function test_savedsearches_history() { | |||
|     var options = PlacesUtils.history.getNewQueryOptions(); | ||||
|     options.expandQueries = 1; | ||||
|     var query = PlacesUtils.history.getNewQuery(); | ||||
|     query.setFolders([testRoot], 1); | ||||
|     query.setFolders([testRootId], 1); | ||||
|     var result = PlacesUtils.history.executeQuery(query, options); | ||||
|     var rootNode = result.root; | ||||
|     rootNode.containerOpen = true; | ||||
|  | @ -148,7 +156,7 @@ add_task(async function test_savedsearches_history() { | |||
|       // test that query node type is container when expandQueries=1
 | ||||
|       do_check_eq(node.type, node.RESULT_TYPE_QUERY); | ||||
|       // test that queries (as containers) have valid itemId
 | ||||
|       do_check_eq(node.itemId, searchId); | ||||
|       do_check_eq(node.bookmarkGuid, searchItem.guid); | ||||
|       node.QueryInterface(Ci.nsINavHistoryContainerResultNode); | ||||
|       node.containerOpen = true; | ||||
| 
 | ||||
|  | @ -177,26 +185,29 @@ add_task(async function test_savedsearches_history() { | |||
|     } | ||||
| 
 | ||||
|     // test live-update of moved queries
 | ||||
|     var tmpFolderId = PlacesUtils.bookmarks.createFolder( | ||||
|       testRoot, "foo", PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
|     PlacesUtils.bookmarks.moveItem( | ||||
|       searchId, tmpFolderId, PlacesUtils.bookmarks.DEFAULT_INDEX); | ||||
|     let tmpFolder = await PlacesUtils.bookmarks.insert({ | ||||
|       parentGuid: testRoot.guid, | ||||
|       title: "foo", | ||||
|       type: PlacesUtils.bookmarks.TYPE_FOLDER, | ||||
|     }); | ||||
| 
 | ||||
|     searchItem.parentGuid = tmpFolder.guid; | ||||
|     await PlacesUtils.bookmarks.update(searchItem); | ||||
|     var tmpFolderNode = rootNode.getChild(0); | ||||
|     do_check_eq(tmpFolderNode.itemId, tmpFolderId); | ||||
|     do_check_eq(tmpFolderNode.bookmarkGuid, tmpFolder.guid); | ||||
|     tmpFolderNode.QueryInterface(Ci.nsINavHistoryContainerResultNode); | ||||
|     tmpFolderNode.containerOpen = true; | ||||
|     do_check_eq(tmpFolderNode.childCount, 1); | ||||
| 
 | ||||
|     // test live-update of renamed queries
 | ||||
|     PlacesUtils.bookmarks.setItemTitle(searchId, "foo"); | ||||
|     searchItem.title = "foo"; | ||||
|     await PlacesUtils.bookmarks.update(searchItem); | ||||
|     do_check_eq(tmpFolderNode.title, "foo"); | ||||
| 
 | ||||
|     // test live-update of deleted queries
 | ||||
|     PlacesUtils.bookmarks.removeItem(searchId); | ||||
|     try { | ||||
|       tmpFolderNode = root.getChild(1); | ||||
|       do_throw("query was not removed"); | ||||
|     } catch (ex) {} | ||||
|     await PlacesUtils.bookmarks.remove(searchItem); | ||||
|     Assert.throws(() => tmpFolderNode = rootNode.getChild(1), /NS_ERROR_ILLEGAL_VALUE/, | ||||
|       "getting a deleted child should throw"); | ||||
| 
 | ||||
|     tmpFolderNode.containerOpen = false; | ||||
|     rootNode.containerOpen = false; | ||||
|  |  | |||
|  | @ -195,7 +195,10 @@ add_task(async function test_removes_suggestion_if_its_content_is_typed_in() { | |||
|           {content: "bar", description: "second suggestion"}, | ||||
|           {content: "baz", description: "third suggestion"}, | ||||
|         ]); | ||||
|         controller.stopSearch(); | ||||
|         // The API doesn't have a way to notify when addition is complete.
 | ||||
|         do_timeout(1000, () => { | ||||
|           controller.stopSearch(); | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
|  | @ -302,8 +305,11 @@ add_task(async function test_setting_the_default_suggestion() { | |||
|     emit(message, text, id) { | ||||
|       if (message === ExtensionSearchHandler.MSG_INPUT_CHANGED) { | ||||
|         ExtensionSearchHandler.addSuggestions(keyword, id, []); | ||||
|         // The API doesn't have a way to notify when addition is complete.
 | ||||
|         do_timeout(1000, () => { | ||||
|           controller.stopSearch(); | ||||
|         }); | ||||
|       } | ||||
|       controller.stopSearch(); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|  | @ -358,7 +364,10 @@ add_task(async function test_maximum_number_of_suggestions_is_enforced() { | |||
|           {content: "i", description: "ninth suggestion"}, | ||||
|           {content: "j", description: "tenth suggestion"}, | ||||
|         ]); | ||||
|         controller.stopSearch(); | ||||
|         // The API doesn't have a way to notify when addition is complete.
 | ||||
|         do_timeout(1000, () => { | ||||
|           controller.stopSearch(); | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
|  |  | |||
|  | @ -10,12 +10,16 @@ clang_checkers: | |||
|    publish: !!bool yes | ||||
|  - name: clang-analyzer-security.* | ||||
|    publish: !!bool no | ||||
|  - name: misc-argument-comment | ||||
|    publish: !!bool yes | ||||
|  - name: misc-assert-side-effect | ||||
|    publish: !!bool yes | ||||
|  - name: misc-suspicious-missing-comma | ||||
|    publish: !!bool yes | ||||
|  - name: misc-suspicious-semicolon | ||||
|    publish: !!bool yes | ||||
|  - name: misc-unused-using-decls | ||||
|    publish: !!bool yes | ||||
|  - name: modernize-avoid-bind | ||||
|    publish: !!bool yes | ||||
|  - name: modernize-loop-convert | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Andreea Pavel
						Andreea Pavel