From 2a4dbde4b87f1191746370ea6a4799be41bca348 Mon Sep 17 00:00:00 2001 From: sole Date: Fri, 12 May 2017 16:19:43 +0100 Subject: [PATCH 01/30] Bug 1364453 - DevTools: move debugging intermittents documentation into the tree. r=pbro MozReview-Commit-ID: DfcNi6wyRLE --HG-- extra : rebase_source : c12b9c739eb1db8e1b7d041f0a60dfb858235448 --- devtools/docs/SUMMARY.md | 1 + .../docs/tests/debugging-intermittents.md | 67 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 devtools/docs/tests/debugging-intermittents.md diff --git a/devtools/docs/SUMMARY.md b/devtools/docs/SUMMARY.md index a1697445502b..2ad944fc2105 100644 --- a/devtools/docs/SUMMARY.md +++ b/devtools/docs/SUMMARY.md @@ -37,3 +37,4 @@ * [Chrome mochitests](tests/mochitest-chrome.md) * [DevTools mochitests](tests/mochitest-devtools.md) * [Writing tests](tests/writing-tests.md) + * [Debugging intermittent failures](tests/debugging-intermittents.md) diff --git a/devtools/docs/tests/debugging-intermittents.md b/devtools/docs/tests/debugging-intermittents.md new file mode 100644 index 000000000000..41b5d1ba91dc --- /dev/null +++ b/devtools/docs/tests/debugging-intermittents.md @@ -0,0 +1,67 @@ +# Debugging intermittent test failures + +While working on automated tests, you will inevitably encounter intermittent test failures, also called "oranges" here. + +This page documents some tips for finding and debugging these test failures. + +## Finding Intermittents + +Normally you will have no trouble finding out that a particular test is intermittent, because a bug will be filed and you will see it through the normal mechanisms. + +However, it can still be useful to see intermittents in context. The [War on Oranges site](https://brasstacks.mozilla.com/orangefactor/) shows intermittents ranked by frequency. The orange factor robot also posts weekly updates to the relevant bugs in Bugzilla (see [an example here](https://bugzilla.mozilla.org/show_bug.cgi?id=1250523#c4)). + +You can also see oranges in Bugzilla. Go to [the settings page](https://bugzilla.mozilla.org/userprefs.cgi?tab=settings) and enable "When viewing a bug, show its corresponding Orange Factor page". + +## Reproducing Test Failures locally + +The first step to fix an orange is to reproduce it. + +If a test fails at different places for each failure it might be a timeout. The current mochitest timeout is 45 seconds, so if successful runs of an intermittent are ~40 seconds, it might just be a +real timeout. This is particularly true if the failure is most often seen on the slower builds, for example Linux 32 debug. In this case you can either split the test or call `requestLongerTimeout`. + +Sometimes reproducing can only be done in automation, but it's worth trying locally, because this makes it much simpler to debug. + +First, try running the test in isolation. You can use the `--repeat` and `--run-until-failure` flags to `mach mochitest` to automate this a bit. It's nice to do this sort of thing in a VM (or using Xnest on Linux) to avoid locking up your machine. Mozilla provides an [easy-to-use VM](https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Using_the_VM). + +Sometimes, though, a test will only fail if it is run in conjunction with one or more other tests. You can use the `--start-at` and `--end-at` flags with `mach mochitest` to run a group of tests together. + +For some jobs, but not all, you can get an [interactive shell from TaskCluster](https://jonasfj.dk/2016/03/one-click-loaners-with-taskcluster/). + +There's also a [handy page of e10s test debugging tips](https://wiki.mozilla.org/Electrolysis/e10s_test_tips) that is worth a read. + +Because intermittents are often caused by race conditions, it's sometimes useful to enable Chaos Mode. This changes timings and event orderings a bit. The simplest way to do this is to enable it in a specific test, by +calling `SimpleTest.testInChaosMode`. You can also set the `MOZ_CHAOSMODE` environment variable, or even edit `mfbt/ChaosMode.cpp` directly. + +Some tests leak intermittently. Use `ac_add_options --enable-logrefcnt` in your mozconfig to potentially find them. + +The `rr` tool has [its own chaos mode](http://robert.ocallahan.org/2016/02/introducing-rr-chaos-mode.html). This can also sometimes reproduce a failure that isn't ordinarily reproducible. While it's difficult to debug JS bugs using `rr`, often if you can reliably reproduce the failure you can at least experiment (see below) to attempt a fix. + +## That Didn't Work + +If you couldn't reproduce locally, there are other options. + +One useful approach is to add additional logging to the test, then push again. Sometimes log buffering makes the output weird; you can add a call to `SimpleTest.requestCompleteLog()` to fix this. + +You can run a single directory of tests on try using `mach try DIR`. You can also use the `--rebuild` flag to retrigger test jobs multiple times; or you can also do this easily from treeherder. + +## Solving + +Sometimes the problem is a race at a specific spot in the test. You can test this theory by adding a short wait to see if the failure goes away, like: +```javascript +yield new Promise(r => setTimeout(r, 100)); +``` + +See the `waitForTick` and `waitForTime` functions in `DevToolsUtils` for similar functionality. + +You can use a similar trick to "pause" the test at a certain point. This is useful when debugging locally because it will leave Firefox open and responsive, at the specific spot you've chosen. Do this +using `yield new Promise(r => r);`. + +`shared-head.js` also has some helpers, like `once`, to bind to events with additional logging. + +You can also binary search the test by either commenting out chunks of it, or hacking in early `return`s. You can do a bunch of these experiments in parallel without waiting for the first to complete. + +## Verifying + +It's difficult to verify that an intermittent has truly been fixed. +One thing you can do is push to try, and then retrigger the job many times in treeherder. Exactly how many times you should retrigger depends on the frequency of the failure. + From 5f95ff6cfd3223346925c26bf9a2cbb85ab6930d Mon Sep 17 00:00:00 2001 From: Johann Hofmann Date: Thu, 11 May 2017 22:05:01 -0400 Subject: [PATCH 02/30] Bug 1364166 - Only update combined buttons style in the customizableUI panel. r=Gijs This Photon-specific workaround styles the zoom and edit button combinations when the user switches back to the old menu panel. Before this patch, it used to apply the panel style even if the buttons were in the toolbar. This patch fixes that by not even updating the style if the buttons are not in the panel. MozReview-Commit-ID: JZZUTudSDeZ --HG-- extra : rebase_source : 6ea20aba09bfe12a558dc20a6e65f324292ca0f2 --- browser/components/customizableui/CustomizableWidgets.jsm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/browser/components/customizableui/CustomizableWidgets.jsm b/browser/components/customizableui/CustomizableWidgets.jsm index 4f78d7787106..5003ad8de888 100644 --- a/browser/components/customizableui/CustomizableWidgets.jsm +++ b/browser/components/customizableui/CustomizableWidgets.jsm @@ -756,8 +756,9 @@ const CustomizableWidgets = [ // We need it right now for the case where we re-register the old-style // main menu panel if photon is disabled at runtime, and we automatically // put the widgets in there, so they get the right style in the panel. - onAreaNodeRegistered(aArea, aContainer) { + onAreaNodeRegistered: (aArea, aContainer) => { if (aContainer.ownerDocument == node.ownerDocument && + aArea == this.currentArea && aArea == CustomizableUI.AREA_PANEL) { updateCombinedWidgetStyle(node, aArea, true); } @@ -863,8 +864,9 @@ const CustomizableWidgets = [ // We need it right now for the case where we re-register the old-style // main menu panel if photon is disabled at runtime, and we automatically // put the widgets in there, so they get the right style in the panel. - onAreaNodeRegistered(aArea, aContainer) { + onAreaNodeRegistered: (aArea, aContainer) => { if (aContainer.ownerDocument == node.ownerDocument && + aArea == this.currentArea && aArea == CustomizableUI.AREA_PANEL) { updateCombinedWidgetStyle(node, aArea); } From 6acadff2d139bc19cafd94c27094f3c5937fbe13 Mon Sep 17 00:00:00 2001 From: Hemant Singh Patwal Date: Fri, 12 May 2017 20:06:39 +0530 Subject: [PATCH 03/30] Bug 1362421 - Enable eslint on caps/tests/mochitest/browser_checkloaduri.js. r=standard8 MozReview-Commit-ID: ZIec0SdBaa --HG-- extra : rebase_source : 7bf902837ab737ddb8fbeacae96030403fcce57f --- .eslintignore | 5 +---- caps/tests/mochitest/browser_checkloaduri.js | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.eslintignore b/.eslintignore index d73ee0e8283b..1297dcd81b3f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -78,10 +78,7 @@ browser/extensions/activity-stream/data/content/activity-stream.bundle.js browser/extensions/activity-stream/vendor/** # imported from chromium browser/extensions/mortar/** - -# caps/ exclusions -caps/tests/mochitest/browser_checkloaduri.js - + # devtools/ exclusions devtools/client/canvasdebugger/** devtools/client/commandline/** diff --git a/caps/tests/mochitest/browser_checkloaduri.js b/caps/tests/mochitest/browser_checkloaduri.js index 24a97c1c450e..045effeac36f 100644 --- a/caps/tests/mochitest/browser_checkloaduri.js +++ b/caps/tests/mochitest/browser_checkloaduri.js @@ -261,9 +261,13 @@ add_task(function* () { browser, testURL.toString(), function* (testURLFn) { + // eslint-disable-next-line no-shadow , no-eval let testURL = eval("(" + testURLFn + ")"); + // eslint-disable-next-line no-shadow let ssm = Services.scriptSecurityManager; + // eslint-disable-next-line no-shadow let baseFlags = ssm.STANDARD | ssm.DONT_REPORT_ERRORS; + // eslint-disable-next-line no-unused-vars let makeURI = Cu.import("resource://gre/modules/BrowserUtils.jsm", {}).BrowserUtils.makeURI; let b = new content.Blob(["I am a blob"]); let contentBlobURI = content.URL.createObjectURL(b); From 9868cfdd56a13b3a21cbdb73e3bfb4da82b931b6 Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Fri, 12 May 2017 18:20:08 +0800 Subject: [PATCH 04/30] Bug 1364374 - don't test flac on Android. r=jwwang Now we don't support flac on fennec, only need to test it on non-android platforms. MozReview-Commit-ID: 9Qli9zSlNe9 --HG-- extra : rebase_source : 4cc96bc25d499b27f745b20e5ca8bb961a4632dd --- dom/media/test/manifest.js | 23 ++++++++++++++--------- dom/media/test/mochitest.ini | 1 - 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/dom/media/test/manifest.js b/dom/media/test/manifest.js index 7e78fad79ed2..ab30c357424d 100644 --- a/dom/media/test/manifest.js +++ b/dom/media/test/manifest.js @@ -775,17 +775,22 @@ var gMetadataTests = [ }, { name:"wavedata_u8.wav", tags: { } }, - { name:"flac-s24.flac", tags: { - ALBUM:"Seascapes", - TITLE:"(La Mer) - II. Jeux de vagues. Allegro", - COMPOSER:"Debussy, Claude", - TRACKNUMBER:"2/9", - DISCNUMBER:"1/1", - encoder:"Lavf57.41.100", - } - }, ]; +// Now Fennec doesn't support flac, so only test it on non-android platforms. +if (getAndroidVersion() < 0) { + gMetadataTests = gMetadataTests.concat([ + { name:"flac-s24.flac", tags: { + ALBUM:"Seascapes", + TITLE:"(La Mer) - II. Jeux de vagues. Allegro", + COMPOSER:"Debussy, Claude", + TRACKNUMBER:"2/9", + DISCNUMBER:"1/1", + encoder:"Lavf57.41.100", + } + }]); +} + // Test files for Encrypted Media Extensions var gEMETests = [ { diff --git a/dom/media/test/mochitest.ini b/dom/media/test/mochitest.ini index 1adec66631f1..92f8fcc76050 100644 --- a/dom/media/test/mochitest.ini +++ b/dom/media/test/mochitest.ini @@ -870,7 +870,6 @@ skip-if = android_version == '17' # android(bug 1232305) [test_mediatrack_replay_from_end.html] skip-if = toolkit == 'android' # android(bug 1232305) [test_metadata.html] -skip-if = toolkit == 'android' # android(bug 1232305) [test_mixed_principals.html] skip-if = toolkit == 'android' # bug 1309814, android(bug 1232305) [test_mozHasAudio.html] From 48a452243ef929e43531c96243524fa4c5446d32 Mon Sep 17 00:00:00 2001 From: Johann Hofmann Date: Tue, 9 May 2017 18:30:24 -0400 Subject: [PATCH 05/30] Bug 1361686 - Share bookmark toolbar button styling between platforms. r=dao MozReview-Commit-ID: 4LA4AXIud8z --HG-- extra : rebase_source : 76b11ddf032420fdca1e121c4bd22fbbdebdfa83 --- browser/themes/linux/browser.css | 57 ------------- browser/themes/osx/browser.css | 77 +----------------- browser/themes/osx/jar.mn | 2 - browser/themes/osx/places/folderDropArrow.png | Bin 201 -> 0 bytes .../themes/osx/places/folderDropArrow@2x.png | Bin 443 -> 0 bytes browser/themes/shared/jar.inc.mn | 3 + browser/themes/shared/places/arrow-down.svg | 6 ++ browser/themes/shared/toolbarbuttons.inc.css | 28 +++++++ browser/themes/windows/browser.css | 42 +--------- 9 files changed, 43 insertions(+), 172 deletions(-) delete mode 100644 browser/themes/osx/places/folderDropArrow.png delete mode 100644 browser/themes/osx/places/folderDropArrow@2x.png create mode 100644 browser/themes/shared/places/arrow-down.svg diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css index 932ee0841ccd..105e2383884c 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -122,63 +122,6 @@ background-color: -moz-Dialog; } -/* Places toolbar */ -toolbarbutton.bookmark-item:not(.subviewbutton), -#personal-bookmarks[cui-areatype="toolbar"]:not([overflowedItem=true]) > #bookmarks-toolbar-placeholder { - margin: 0; - padding: 2px 3px; -} - -toolbarbutton.bookmark-item:not(.subviewbutton):not(:hover):not(:active):not([open]) { - color: inherit; -} - -toolbarbutton.bookmark-item:not(.subviewbutton) { - -moz-appearance: none; - border: 1px solid transparent; - border-radius: 2px; - transition-property: background-color, border-color; - transition-duration: 150ms; -} - -toolbarbutton.bookmark-item:not(.subviewbutton):hover:not([open]) { - background: var(--toolbarbutton-hover-background); - border-color: var(--toolbarbutton-hover-bordercolor); -} - -toolbarbutton.bookmark-item:not(.subviewbutton):hover:active, -toolbarbutton.bookmark-item[open="true"] { - background: var(--toolbarbutton-active-background); - box-shadow: var(--toolbarbutton-active-boxshadow); - border-color: var(--toolbarbutton-active-bordercolor); -} - -toolbarbutton.bookmark-item:not(.subviewbutton):hover:-moz-lwtheme { - background: var(--toolbarbutton-hover-background); - border-color: var(--toolbarbutton-hover-bordercolor); -} - -.bookmark-item > .toolbarbutton-icon, -#personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-icon { - width: 16px; - height: 16px; -} - -/* Force the display of the label for bookmarks */ -.bookmark-item > .toolbarbutton-text, -#personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-text { - display: -moz-box !important; -} - -.bookmark-item > .toolbarbutton-menu-dropmarker { - display: none; -} - -/* Dropmarker for folder bookmarks */ -.bookmark-item[container] > .toolbarbutton-menu-dropmarker { - display: -moz-box !important; -} - #bookmarks-toolbar-placeholder { list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png") !important; } diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css index b5ae91462b6e..8923336c1a36 100644 --- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -313,84 +313,11 @@ toolbarbutton.chevron:-moz-locale-dir(rtl) > .toolbarbutton-icon { /* ----- BOOKMARK BUTTONS ----- */ -toolbarbutton.bookmark-item:not(.subviewbutton), -#personal-bookmarks[cui-areatype="toolbar"]:not([overflowedItem=true]) > #bookmarks-toolbar-placeholder { - border: 0; - border-radius: 10000px; - padding: 1px 8px; - margin: 0 0 1px; -} - -#personal-bookmarks[cui-areatype="toolbar"]:not([overflowedItem=true]) > #bookmarks-toolbar-placeholder { - -moz-box-orient: horizontal; -} - .bookmark-item > .toolbarbutton-menu-dropmarker { - list-style-image: url("chrome://browser/skin/places/folderDropArrow.png"); - -moz-image-region: rect(0, 7px, 5px, 0); + list-style-image: url("chrome://browser/skin/places/arrow-down.svg"); + fill: currentColor; margin-top: 1px; margin-inline-start: 3px; - margin-inline-end: -2px; -} - -@media (min-resolution: 2dppx) { - .bookmark-item > .toolbarbutton-menu-dropmarker { - list-style-image: url("chrome://browser/skin/places/folderDropArrow@2x.png"); - -moz-image-region: rect(0, 14px, 10px, 0); - } - - .bookmark-item > .toolbarbutton-menu-dropmarker > .dropmarker-icon { - width: 7px; - } -} - -.bookmark-item > .toolbarbutton-text, -#personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-text { - display: -moz-box !important; /* Force the display of the label for bookmarks */ -} - -toolbarbutton.bookmark-item:not(.subviewbutton):hover { - background-color: rgba(0, 0, 0, .205); -} - -toolbarbutton.bookmark-item:hover:not(.subviewbutton), -toolbarbutton.bookmark-item[open="true"]:not(.subviewbutton) { - color: #FFF !important; - text-shadow: 0 1px rgba(0, 0, 0, .4) !important; -} - -.bookmark-item:hover > .toolbarbutton-menu-dropmarker, -.bookmark-item[open="true"] > .toolbarbutton-menu-dropmarker { - -moz-image-region: rect(5px, 7px, 10px, 0); -} - -@media (min-resolution: 2dppx) { - .bookmark-item:hover > .toolbarbutton-menu-dropmarker, - .bookmark-item[open="true"] > .toolbarbutton-menu-dropmarker { - -moz-image-region: rect(10px, 14px, 20px, 0); - } -} - -toolbarbutton.bookmark-item:not(.subviewbutton):active:hover, -toolbarbutton.bookmark-item:not(.subviewbutton)[open="true"] { - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.4), 0 1px rgba(255, 255, 255, 0.4); - background-color: rgba(0, 0, 0, .5); -} - -toolbarbutton.bookmark-item > menupopup { - margin-inline-start: 3px; -} - -.bookmark-item > .toolbarbutton-icon, -#personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-icon { - width: 16px; - min-height: 16px; - max-height: 16px; -} - -.bookmark-item > .toolbarbutton-icon[label]:not([label=""]), -.bookmark-item > .toolbarbutton-icon[type="menu"] { - margin-inline-end: 5px; } .bookmark-item[container] { diff --git a/browser/themes/osx/jar.mn b/browser/themes/osx/jar.mn index bc3fc3b03504..7fc7cd7ef1b8 100644 --- a/browser/themes/osx/jar.mn +++ b/browser/themes/osx/jar.mn @@ -87,8 +87,6 @@ browser.jar: skin/classic/browser/places/history@2x.png (places/history@2x.png) skin/classic/browser/places/toolbar.png (places/toolbar.png) skin/classic/browser/places/toolbarDropMarker.png (places/toolbarDropMarker.png) - skin/classic/browser/places/folderDropArrow.png (places/folderDropArrow.png) - skin/classic/browser/places/folderDropArrow@2x.png (places/folderDropArrow@2x.png) skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css) skin/classic/browser/places/starred48.png (places/starred48.png) skin/classic/browser/places/starred48@2x.png (places/starred48@2x.png) diff --git a/browser/themes/osx/places/folderDropArrow.png b/browser/themes/osx/places/folderDropArrow.png deleted file mode 100644 index 8d722ccd5e6e62e89c00097c32d17ace829e4d0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)L!2%>N9qnlYQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>Jin2Xj978ywlP}Cluetqd*HzQiua9lt`podp^{;oN_Sq+v zKdC#v_J66Qgv7tu$IkTK6Wa7>za}%wpX+CHT8j@bc^Mp=_Mc-zYvaMkS9|}&UX+pO3|NC`?J&G*Lt|mW@VPR)Eqaebd7jIs8fITn~Xf1=MtDnm{r-UW|hfGh? diff --git a/browser/themes/osx/places/folderDropArrow@2x.png b/browser/themes/osx/places/folderDropArrow@2x.png deleted file mode 100644 index 9efb6d95d8b33ac99aa364126b568b954fe01960..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 443 zcmV;s0Yv_ZP)&r z))>VQf{etYu$6*@*xW?ICJe^JBxqHLLZLyY(+Gux(8**wg<7K!ugFBaLZR70vdQ`@ zgqgwj>&~gpL8kKMQQ0RR705)EZ<<{;SWmLSj(7e#8<}V2-ep!;OtRu#y70*aiDw6r2s4%{4?=e{Y|*Oub86|3rx!Bsb77#(c_z^yVf;+@dFbe13!IT z+s|=b>FPP&y~A%R`FMmy6~iNY|4&{{BQe5MV + + + diff --git a/browser/themes/shared/toolbarbuttons.inc.css b/browser/themes/shared/toolbarbuttons.inc.css index 7cc7dd7410ee..e6b45db1b071 100644 --- a/browser/themes/shared/toolbarbuttons.inc.css +++ b/browser/themes/shared/toolbarbuttons.inc.css @@ -146,6 +146,7 @@ toolbar[brighttext] .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker { @conditionalForwardWithUrlbar@ > .toolbarbutton-1:-moz-any([disabled],:not([open]):not([disabled]):not(:active)) > .toolbarbutton-icon, %endif .findbar-button > .toolbarbutton-text, +toolbarbutton.bookmark-item:not(.subviewbutton), #nav-bar .toolbarbutton-1 > .toolbarbutton-icon, #nav-bar .toolbarbutton-1 > .toolbarbutton-text, #nav-bar .toolbarbutton-1 > .toolbarbutton-badge-stack, @@ -256,6 +257,7 @@ toolbar[brighttext] .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker { .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled=true]):hover, .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled=true]):hover, .findbar-button:not(:-moz-any([checked="true"],[disabled="true"])):hover > .toolbarbutton-text, +toolbarbutton.bookmark-item:not(.subviewbutton):hover:not([disabled="true"]):not([open]), #nav-bar .toolbarbutton-1:not([disabled=true]) > .toolbarbutton-menubutton-button[open] + .toolbarbutton-menubutton-dropmarker > .dropmarker-icon, #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-button > .toolbarbutton-icon, #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon, @@ -272,6 +274,8 @@ toolbar[brighttext] .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker { } .findbar-button:not([disabled=true]):-moz-any([checked="true"],:hover:active) > .toolbarbutton-text, +toolbarbutton.bookmark-item:not(.subviewbutton):hover:active:not([disabled="true"]), +toolbarbutton.bookmark-item[open="true"], #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):-moz-any(:hover:active, [open]) > .toolbarbutton-icon, #nav-bar .toolbarbutton-1[open] > .toolbarbutton-menubutton-dropmarker:not([disabled=true]) > .dropmarker-icon, #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon, @@ -396,3 +400,27 @@ toolbar[brighttext] .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker { margin-bottom: -20px; } +/* ::::: bookmark buttons ::::: */ + +toolbarbutton.bookmark-item:not(.subviewbutton) { + margin: 0; + padding: 2px 3px; + -moz-appearance: none; +} + +.bookmark-item > .toolbarbutton-icon, +#personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-icon { + width: 16px; + height: 16px; +} + +/* Force the display of the label for bookmarks */ +.bookmark-item > .toolbarbutton-text, +#personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-text { + display: -moz-box !important; +} + +.bookmark-item > .toolbarbutton-icon[label]:not([label=""]), +.bookmark-item > .toolbarbutton-icon[type="menu"] { + margin-inline-end: 5px; +} diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index 1dae15aa1a76..824185da8fbe 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -371,45 +371,11 @@ toolbar[brighttext] { /* ::::: bookmark buttons ::::: */ -toolbarbutton.bookmark-item:not(.subviewbutton), -#personal-bookmarks[cui-areatype="toolbar"]:not([overflowedItem=true]) > #bookmarks-toolbar-placeholder { - margin: 0; - padding: 2px 3px; - -moz-appearance: none; - border: 1px solid transparent; - border-radius: var(--toolbarbutton-border-radius); - background-origin: padding-box !important; - background-clip: padding-box !important; - transition-property: background-color, border-color, box-shadow; - transition-duration: 150ms; -} - -toolbarbutton.bookmark-item:not(.subviewbutton):hover:not([disabled="true"]):not([open]) { - border-color: var(--toolbarbutton-hover-bordercolor); - background: var(--toolbarbutton-hover-background); -} - -toolbarbutton.bookmark-item:not(.subviewbutton):hover:active:not([disabled="true"]), -toolbarbutton.bookmark-item[open="true"] { - border-color: var(--toolbarbutton-active-bordercolor); - box-shadow: var(--toolbarbutton-active-boxshadow); - background: var(--toolbarbutton-active-background); -} - -.bookmark-item > .toolbarbutton-icon, -#personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-icon { - width: 16px; - height: 16px; -} - -/* Force the display of the label for bookmarks */ -.bookmark-item > .toolbarbutton-text, -#personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-text { - display: -moz-box !important; -} - .bookmark-item > .toolbarbutton-menu-dropmarker { - display: none; + list-style-image: url("chrome://browser/skin/places/arrow-down.svg"); + fill: currentColor; + margin-top: 1px; + margin-inline-start: 3px; } #bookmarks-toolbar-placeholder { From aa39098b9237e8329a34e3b567406c82fc02abd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Sat, 13 May 2017 20:34:27 +0200 Subject: [PATCH 06/30] Bug 1364702 - [RTL] mirror the back button in toolbarbutton-icons.inc.css like other buttons, and also mirror the forward button for Photon (non-Photon forward button is handled in browser.css). r=johannh MozReview-Commit-ID: 4NyxmmYlmcM --HG-- extra : rebase_source : 51ada5ba87e2aea8f08f4a6a427fe0a56a660517 --- browser/themes/shared/toolbarbutton-icons.inc.css | 8 ++++++++ browser/themes/shared/toolbarbuttons.inc.css | 4 ---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/browser/themes/shared/toolbarbutton-icons.inc.css b/browser/themes/shared/toolbarbutton-icons.inc.css index 32cb11c57176..4059ad69f57a 100644 --- a/browser/themes/shared/toolbarbutton-icons.inc.css +++ b/browser/themes/shared/toolbarbutton-icons.inc.css @@ -17,11 +17,19 @@ toolbar[brighttext] :-moz-any(@primaryToolbarButtons@) { list-style-image: url("chrome://browser/skin/back-large.svg"); } +#back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon { + transform: scaleX(-1); +} + #forward-button { list-style-image: url("chrome://browser/skin/forward.svg"); } %ifdef MOZ_PHOTON_THEME +#forward-button:-moz-locale-dir(rtl) > .toolbarbutton-icon { + transform: scaleX(-1); +} + #reload-button { list-style-image: url("chrome://browser/skin/reload.svg"); } diff --git a/browser/themes/shared/toolbarbuttons.inc.css b/browser/themes/shared/toolbarbuttons.inc.css index e6b45db1b071..4f195d069fa6 100644 --- a/browser/themes/shared/toolbarbuttons.inc.css +++ b/browser/themes/shared/toolbarbuttons.inc.css @@ -366,10 +366,6 @@ toolbarbutton.bookmark-item[open="true"], padding: 7px !important; } -#back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon { - transform: scaleX(-1); -} - %ifdef MOZ_PHOTON_THEME #back-button:not(:hover):not(:active):not([open=true]) > .toolbarbutton-icon, #back-button[disabled=true] > .toolbarbutton-icon { From 22aea6592af1ca798fb3efe93c9508ddf4a31ec2 Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Fri, 12 May 2017 17:28:48 +0200 Subject: [PATCH 07/30] Bug 1345349 - Places keywords break if one of the keywords points to an invalid url r=standard8 MozReview-Commit-ID: IbTNqPrreAR --HG-- extra : rebase_source : fde73d1090fd1de5547d31c5042c7b5aca2d30ca --- toolkit/components/places/PlacesUtils.jsm | 27 +++++++++---- .../places/tests/unit/test_keywords.js | 40 ++++++++++++++----- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/toolkit/components/places/PlacesUtils.jsm b/toolkit/components/places/PlacesUtils.jsm index a266543bbc52..fcd2018f70d8 100644 --- a/toolkit/components/places/PlacesUtils.jsm +++ b/toolkit/components/places/PlacesUtils.jsm @@ -1505,8 +1505,8 @@ this.PlacesUtils = { * Your custom queries can - and will - break overtime. * * Example: - * let db = yield PlacesUtils.promiseDBConnection(); - * let rows = yield db.executeCached(sql, params); + * let db = await PlacesUtils.promiseDBConnection(); + * let rows = await db.executeCached(sql, params); */ promiseDBConnection: () => gAsyncDBConnPromised, @@ -1526,7 +1526,7 @@ this.PlacesUtils = { * operations are complete before proceeding. * * Example: - * yield withConnectionWrapper("Bookmarks: Remove a bookmark", Task.async(function*(db) { + * await withConnectionWrapper("Bookmarks: Remove a bookmark", Task.async(function*(db) { * // Proceed with the db, asynchronously. * // Shutdown will not interrupt operations that take place here. * })); @@ -2408,12 +2408,25 @@ XPCOMUtils.defineLazyGetter(this, "gKeywordsCachePromise", () => FROM moz_keywords k JOIN moz_places h ON h.id = k.place_id `); + let brokenKeywords = []; for (let row of rows) { let keyword = row.getResultByName("keyword"); - let entry = { keyword, - url: new URL(row.getResultByName("url")), - postData: row.getResultByName("post_data") }; - cache.set(keyword, entry); + try { + let entry = { keyword, + url: new URL(row.getResultByName("url")), + postData: row.getResultByName("post_data") }; + cache.set(keyword, entry); + } catch (ex) { + // The url is invalid, don't load the keyword and remove it, or it + // would break the whole keywords API. + brokenKeywords.push(keyword); + } + } + if (brokenKeywords.length) { + await db.execute( + `DELETE FROM moz_keywords + WHERE keyword IN (${brokenKeywords.map(JSON.stringify).join(",")}) + `); } // Helper to get a keyword from an href. diff --git a/toolkit/components/places/tests/unit/test_keywords.js b/toolkit/components/places/tests/unit/test_keywords.js index e227ddda6bfc..98c7644e1667 100644 --- a/toolkit/components/places/tests/unit/test_keywords.js +++ b/toolkit/components/places/tests/unit/test_keywords.js @@ -19,7 +19,11 @@ async function check_keyword(aExpectExists, aHref, aKeyword, aPostData = null) { } else { Assert.ok(!entry || entry.url.href != aHref, "The given keyword entry should not exist"); - Assert.equal(null, await PlacesUtils.keywords.fetch({ keyword: aKeyword, url: aHref })); + if (aHref) { + Assert.equal(null, await PlacesUtils.keywords.fetch({ keyword: aKeyword, url: aHref })); + } else { + Assert.equal(null, await PlacesUtils.keywords.fetch({ keyword: aKeyword })); + } } } @@ -79,6 +83,30 @@ function expectBookmarkNotifications() { return observer; } +// This test must be the first one, since it creates the keywords cache. +add_task(async function test_invalidURL() { + await PlacesTestUtils.addVisits("http://test.com/"); + // Change to url to an invalid one, there's no API for that, so we must do + // that manually. + await PlacesUtils.withConnectionWrapper("test_invalidURL", async function(db) { + await db.execute( + `UPDATE moz_places SET url = :broken, url_hash = hash(:broken) + WHERE id = (SELECT id FROM moz_places WHERE url_hash = hash(:url))`, + { url: "http://test.com/", broken: "" }); + + await db.execute( + `INSERT INTO moz_keywords (keyword, place_id) + VALUES (:kw, (SELECT id FROM moz_places WHERE url_hash = hash(:broken)))`, + { broken: "", kw: "keyword" }); + }); + await check_keyword(false, "http://broken.com/", "keyword"); + await check_keyword(false, null, "keyword"); + await PlacesUtils.withConnectionWrapper("test_invalidURL", async function(db) { + let rows = await db.execute(`SELECT * FROM moz_keywords`); + Assert.equal(rows.length, 0, "The broken keyword should have been removed"); + }); +}); + add_task(async function test_invalid_input() { Assert.throws(() => PlacesUtils.keywords.fetch(null), /Invalid keyword/); @@ -174,7 +202,6 @@ add_task(async function test_addBookmarkAndKeyword() { await check_keyword(false, "http://example.com/", "keyword"); let fc = await foreign_count("http://example.com/"); let bookmark = await PlacesUtils.bookmarks.insert({ url: "http://example.com/", - type: PlacesUtils.bookmarks.TYPE_BOOKMARK, parentGuid: PlacesUtils.bookmarks.unfiledGuid }); let observer = expectBookmarkNotifications(); @@ -270,7 +297,6 @@ add_task(async function test_addBookmarkToURIHavingKeyword() { observer = expectBookmarkNotifications(); let bookmark = await PlacesUtils.bookmarks.insert({ url: "http://example.com/", - type: PlacesUtils.bookmarks.TYPE_BOOKMARK, parentGuid: PlacesUtils.bookmarks.unfiledGuid }); Assert.equal((await foreign_count("http://example.com/")), fc + 2); // +1 bookmark observer.check([]); @@ -292,11 +318,9 @@ add_task(async function test_addBookmarkToURIHavingKeyword() { add_task(async function test_sameKeywordDifferentURL() { let fc1 = await foreign_count("http://example1.com/"); let bookmark1 = await PlacesUtils.bookmarks.insert({ url: "http://example1.com/", - type: PlacesUtils.bookmarks.TYPE_BOOKMARK, parentGuid: PlacesUtils.bookmarks.unfiledGuid }); let fc2 = await foreign_count("http://example2.com/"); let bookmark2 = await PlacesUtils.bookmarks.insert({ url: "http://example2.com/", - type: PlacesUtils.bookmarks.TYPE_BOOKMARK, parentGuid: PlacesUtils.bookmarks.unfiledGuid }); await PlacesUtils.keywords.insert({ keyword: "keyword", url: "http://example1.com/" }); @@ -358,7 +382,6 @@ add_task(async function test_sameURIDifferentKeyword() { let observer = expectBookmarkNotifications(); let bookmark = await PlacesUtils.bookmarks.insert({ url: "http://example.com/", - type: PlacesUtils.bookmarks.TYPE_BOOKMARK, parentGuid: PlacesUtils.bookmarks.unfiledGuid }); await PlacesUtils.keywords.insert({keyword: "keyword", url: "http://example.com/" }); @@ -423,10 +446,8 @@ add_task(async function test_deleteKeywordMultipleBookmarks() { let observer = expectBookmarkNotifications(); let bookmark1 = await PlacesUtils.bookmarks.insert({ url: "http://example.com/", - type: PlacesUtils.bookmarks.TYPE_BOOKMARK, parentGuid: PlacesUtils.bookmarks.unfiledGuid }); let bookmark2 = await PlacesUtils.bookmarks.insert({ url: "http://example.com/", - type: PlacesUtils.bookmarks.TYPE_BOOKMARK, parentGuid: PlacesUtils.bookmarks.unfiledGuid }); await PlacesUtils.keywords.insert({ keyword: "keyword", url: "http://example.com/" }); @@ -489,7 +510,6 @@ add_task(async function test_multipleKeywordsSamePostData() { add_task(async function test_oldPostDataAPI() { let bookmark = await PlacesUtils.bookmarks.insert({ url: "http://example.com/", - type: PlacesUtils.bookmarks.TYPE_BOOKMARK, parentGuid: PlacesUtils.bookmarks.unfiledGuid }); await PlacesUtils.keywords.insert({ keyword: "keyword", url: "http://example.com/" }); let itemId = await PlacesUtils.promiseItemId(bookmark.guid); @@ -505,7 +525,6 @@ add_task(async function test_oldPostDataAPI() { add_task(async function test_oldKeywordsAPI() { let bookmark = await PlacesUtils.bookmarks.insert({ url: "http://example.com/", - type: PlacesUtils.bookmarks.TYPE_BOOKMARK, parentGuid: PlacesUtils.bookmarks.unfiledGuid }); await check_keyword(false, "http://example.com/", "keyword"); let itemId = await PlacesUtils.promiseItemId(bookmark.guid); @@ -532,7 +551,6 @@ add_task(async function test_bookmarkURLChange() { let fc1 = await foreign_count("http://example1.com/"); let fc2 = await foreign_count("http://example2.com/"); let bookmark = await PlacesUtils.bookmarks.insert({ url: "http://example1.com/", - type: PlacesUtils.bookmarks.TYPE_BOOKMARK, parentGuid: PlacesUtils.bookmarks.unfiledGuid }); await PlacesUtils.keywords.insert({ keyword: "keyword", url: "http://example1.com/" }); From 99db7d58be489896a9634911ddf575eff249238a Mon Sep 17 00:00:00 2001 From: tiago Date: Sun, 14 May 2017 21:05:12 -0300 Subject: [PATCH 08/30] Bug 1364211 - Correct variable scope in TestSize class for start_size variable. r=automatedtester MozReview-Commit-ID: DvzhvrbsgS6 --HG-- extra : rebase_source : 0ec619f2e702c16a3b50d39bfbd8a5d1156a8fa3 --- .../harness/marionette_harness/tests/unit/test_window_rect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_rect.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_rect.py index 0710e9bf115f..a2067aadfe69 100644 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_rect.py +++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_rect.py @@ -121,8 +121,8 @@ class TestSize(MarionetteTestCase): # so reset to original size minus 1 pixel width start_size = self.marionette.window_size if start_size["width"] == self.max["width"] and start_size["height"] == self.max["height"]: - self.start_size["width"] -= 1 - self.start_size["height"] -= 1 + start_size["width"] -= 1 + start_size["height"] -= 1 self.marionette.set_window_size(start_size["width"], start_size["height"]) self.original_size = self.marionette.window_size From 7fb5541c1edd8f53a728245433f203ee0e2dd3c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Wed, 10 May 2017 16:32:05 +0200 Subject: [PATCH 09/30] Bug 1363739 - Remove search-arrow-go-rtl.svg. r=nhnt11 MozReview-Commit-ID: 6Qvm7an8Uce --HG-- extra : rebase_source : 87a5298118820a01d75144606192849dc994ce59 --- browser/base/content/abouthome/aboutHome.css | 18 ++---------------- browser/base/content/newtab/newTab.css | 14 ++------------ browser/themes/shared/jar.inc.mn | 1 - .../shared/search/search-arrow-go-rtl.svg | 8 -------- 4 files changed, 4 insertions(+), 37 deletions(-) delete mode 100644 browser/themes/shared/search/search-arrow-go-rtl.svg diff --git a/browser/base/content/abouthome/aboutHome.css b/browser/base/content/abouthome/aboutHome.css index f0b2c25f85ba..16eb053bbeae 100644 --- a/browser/base/content/abouthome/aboutHome.css +++ b/browser/base/content/abouthome/aboutHome.css @@ -114,9 +114,8 @@ a { fill: #616366; padding: 0; border: 1px solid; - border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2); + border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2) transparent; border-radius: 0 2px 2px 0; - border-inline-start: 1px solid transparent; box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset, 0 1px 0 hsla(0,0%,100%,.2); cursor: pointer; @@ -126,8 +125,7 @@ a { } #searchSubmit:dir(rtl) { - border-radius: 2px 0 0 2px; - background-image: url("chrome://browser/skin/search-arrow-go-rtl.svg"), linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)); + transform: scaleX(-1); } #searchText:focus + #searchSubmit, @@ -147,13 +145,6 @@ a { 0 1px 0 hsla(210,54%,20%,.03); } -#searchText:focus + #searchSubmit:dir(rtl), -#searchText[keepfocus] + #searchSubmit:dir(rtl), -#searchText[autofocus] + #searchSubmit:dir(rtl) { - background-image: url("chrome://browser/skin/search-arrow-go-rtl.svg"), linear-gradient(#4cb1ff, #1793e5); - fill: white; -} - #searchText + #searchSubmit:hover { background-image: url("chrome://browser/skin/search-arrow-go.svg"), linear-gradient(#66bdff, #0d9eff); fill: white; @@ -163,11 +154,6 @@ a { 0 0 4px hsla(206,100%,20%,.2); } -#searchText + #searchSubmit:dir(rtl):hover { - background-image: url("chrome://browser/skin/search-arrow-go-rtl.svg"), linear-gradient(#66bdff, #0d9eff); - fill: white; -} - #searchText + #searchSubmit:hover:active { box-shadow: 0 1px 1px hsla(211,79%,6%,.1) inset, 0 0 1px hsla(211,79%,6%,.2) inset; diff --git a/browser/base/content/newtab/newTab.css b/browser/base/content/newtab/newTab.css index fcf7597931cd..4a3c4c50c013 100644 --- a/browser/base/content/newtab/newTab.css +++ b/browser/base/content/newtab/newTab.css @@ -307,9 +307,8 @@ body.compact #newtab-search-container { fill: #616366; padding: 0; border: 1px solid; - border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2); + border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2) transparent; border-radius: 0 2px 2px 0; - border-inline-start: 1px solid transparent; box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset, 0 1px 0 hsla(0,0%,100%,.2); cursor: pointer; @@ -319,8 +318,7 @@ body.compact #newtab-search-container { } #newtab-search-submit:dir(rtl) { - border-radius: 2px 0 0 2px; - background-image: url("chrome://browser/skin/search-arrow-go-rtl.svg"), linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)); + transform: scaleX(-1); } #newtab-search-text:focus + #newtab-search-submit, @@ -354,14 +352,6 @@ body.compact #newtab-search-container { transition-duration: 0ms; } -#newtab-search-text:focus + #newtab-search-submit:dir(rtl), -#newtab-search-text[keepfocus] + #newtab-search-submit:dir(rtl), -#newtab-search-text[autofocus] + #newtab-search-submit:dir(rtl), -#newtab-search-text + #newtab-search-submit:dir(rtl):hover { - background-image: url("chrome://browser/skin/search-arrow-go-rtl.svg"), linear-gradient(#4cb1ff, #1793e5); - fill: white; -} - /* CUSTOMIZE */ #newtab-customize-overlay { opacity: 0; diff --git a/browser/themes/shared/jar.inc.mn b/browser/themes/shared/jar.inc.mn index b925173ab65e..51992915aca0 100644 --- a/browser/themes/shared/jar.inc.mn +++ b/browser/themes/shared/jar.inc.mn @@ -142,7 +142,6 @@ skin/classic/browser/search-history-icon.svg (../shared/search/history-icon.svg) skin/classic/browser/search-indicator-magnifying-glass.svg (../shared/search/search-indicator-magnifying-glass.svg) skin/classic/browser/search-arrow-go.svg (../shared/search/search-arrow-go.svg) - skin/classic/browser/search-arrow-go-rtl.svg (../shared/search/search-arrow-go-rtl.svg) skin/classic/browser/gear.svg (../shared/search/gear.svg) skin/classic/browser/tabbrowser/connecting.png (../shared/tabbrowser/connecting.png) skin/classic/browser/tabbrowser/connecting@2x.png (../shared/tabbrowser/connecting@2x.png) diff --git a/browser/themes/shared/search/search-arrow-go-rtl.svg b/browser/themes/shared/search/search-arrow-go-rtl.svg deleted file mode 100644 index 2845eaa2af01..000000000000 --- a/browser/themes/shared/search/search-arrow-go-rtl.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - From ab5433c222cc9c94f0655432b764701d60451000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Mon, 15 May 2017 09:41:42 +0200 Subject: [PATCH 10/30] Bug 1364824: Implement ServoRestyleManager::PostRebuildAllStyleDataEvent. r=heycam MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MozReview-Commit-ID: I9HmM9ZMB2c Signed-off-by: Emilio Cobos Álvarez --HG-- extra : rebase_source : 08481f2a84508ed79d544fefbf39a92cac0d5ce5 --- layout/base/ServoRestyleManager.cpp | 4 +++- layout/reftests/font-face/reftest.list | 2 +- layout/reftests/font-loading-api/reftest.list | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/layout/base/ServoRestyleManager.cpp b/layout/base/ServoRestyleManager.cpp index a311f94bd4e2..cd3287f0e85b 100644 --- a/layout/base/ServoRestyleManager.cpp +++ b/layout/base/ServoRestyleManager.cpp @@ -95,7 +95,9 @@ void ServoRestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint, nsRestyleHint aRestyleHint) { - NS_WARNING("stylo: ServoRestyleManager::PostRebuildAllStyleDataEvent not implemented"); + // TODO(emilio): We should do the stylesheet flushing + device reset async + // here. + RebuildAllStyleData(aExtraHint, aRestyleHint); } /* static */ void diff --git a/layout/reftests/font-face/reftest.list b/layout/reftests/font-face/reftest.list index cd1578b57bfe..c806c000aff2 100644 --- a/layout/reftests/font-face/reftest.list +++ b/layout/reftests/font-face/reftest.list @@ -75,7 +75,7 @@ fails-if(stylo) HTTP(..) == enable-sheet-4.html enable-sheet-4-ref.html # bug 13 skip-if(stylo) HTTP(..) == enable-sheet-5.html enable-sheet-4-ref.html skip HTTP(..) == enable-sheet-6.html multiple-in-family-1-ref.html skip HTTP(..) == enable-sheet-7.html multiple-in-family-1-ref.html -fails-if(stylo) HTTP(..) == disable-sheet-1.html disable-sheet-1-ref.html +HTTP(..) == disable-sheet-1.html disable-sheet-1-ref.html # We're missing disable-sheet-{2,3,6,7} (analogs to # enable-sheet{2,3,6,7}) because I don't know how to detect test # completion for those cases. diff --git a/layout/reftests/font-loading-api/reftest.list b/layout/reftests/font-loading-api/reftest.list index f30525e3ac6d..0a8c46edba73 100644 --- a/layout/reftests/font-loading-api/reftest.list +++ b/layout/reftests/font-loading-api/reftest.list @@ -1,7 +1,7 @@ default-preferences pref(layout.css.font-loading-api.enabled,true) -fails-if(stylo) HTTP(..) == dynamic-insert-1.html dynamic-insert-1-ref.html -fails-if(stylo) HTTP(..) == dynamic-remove-1.html dynamic-remove-1-ref.html +HTTP(..) == dynamic-insert-1.html dynamic-insert-1-ref.html +HTTP(..) == dynamic-remove-1.html dynamic-remove-1-ref.html fails-if(stylo) HTTP(..) == ex-unit-1.html ../font-face/ex-unit-1-ref.html fuzzy-if(skiaContent,2,10) HTTP(..) == multiple-sets-1.html multiple-sets-1-ref.html HTTP(..) == name-collision.html ../font-face/name-collision-ref.html From f0cf11780906c41a0380a7b59a078381727986b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Mon, 15 May 2017 05:28:50 -0500 Subject: [PATCH 11/30] servo: Merge #16835 - Stylo: Bug 1350175 - Support getting line / column number of CSS rules (from ferjm:bug1350175.line.column.css.rules); r=upsuper,SimonSapin - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors Bugzilla bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1350175 Source-Repo: https://github.com/servo/servo Source-Revision: 5cd8265f9a279e1cebe84218e691e7f80a541fb9 --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 4c9d1793c4fd817320f83a695b8701118568d5ef --- servo/Cargo.lock | 1 + servo/components/style/encoding_support.rs | 3 +- servo/components/style/font_face.rs | 11 ++- .../style/gecko/generated/bindings.rs | 28 ++++-- servo/components/style/gecko/rules.rs | 4 +- servo/components/style/stylesheets.rs | 85 +++++++++++++------ servo/ports/geckolib/glue.rs | 43 ++++++---- servo/tests/unit/gfx/Cargo.toml | 1 + servo/tests/unit/gfx/font_cache_thread.rs | 5 ++ servo/tests/unit/gfx/lib.rs | 1 + servo/tests/unit/style/stylesheets.rs | 20 ++++- servo/tests/unit/style/stylist.rs | 5 ++ 12 files changed, 151 insertions(+), 56 deletions(-) diff --git a/servo/Cargo.lock b/servo/Cargo.lock index cb09c8d5ba47..24353999f284 100644 --- a/servo/Cargo.lock +++ b/servo/Cargo.lock @@ -1024,6 +1024,7 @@ dependencies = [ name = "gfx_tests" version = "0.0.1" dependencies = [ + "cssparser 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", "gfx 0.0.1", "ipc-channel 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "style 0.0.1", diff --git a/servo/components/style/encoding_support.rs b/servo/components/style/encoding_support.rs index 92620378b304..af37e20889ad 100644 --- a/servo/components/style/encoding_support.rs +++ b/servo/components/style/encoding_support.rs @@ -88,6 +88,7 @@ impl Stylesheet { &string, url_data, stylesheet_loader, - error_reporter) + error_reporter, + 0) } } diff --git a/servo/components/style/font_face.rs b/servo/components/style/font_face.rs index 94dcc61212d0..0bd6640488ec 100644 --- a/servo/components/style/font_face.rs +++ b/servo/components/style/font_face.rs @@ -12,6 +12,7 @@ use computed_values::{font_style, font_weight, font_stretch}; use computed_values::font_family::FamilyName; use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; +use cssparser::SourceLocation; #[cfg(feature = "gecko")] use gecko_bindings::structs::CSSFontFaceDescriptors; #[cfg(feature = "gecko")] use cssparser::UnicodeRange; use parser::{ParserContext, log_css_error, Parse}; @@ -74,8 +75,10 @@ impl ToCss for UrlSource { /// Parse the block inside a `@font-face` rule. /// /// Note that the prelude parsing code lives in the `stylesheets` module. -pub fn parse_font_face_block(context: &ParserContext, input: &mut Parser) -> FontFaceRuleData { +pub fn parse_font_face_block(context: &ParserContext, input: &mut Parser, location: SourceLocation) + -> FontFaceRuleData { let mut rule = FontFaceRuleData::empty(); + rule.source_location = location; { let parser = FontFaceRuleParser { context: context, @@ -186,6 +189,8 @@ macro_rules! font_face_descriptors_common { #[$doc] pub $ident: Option<$ty>, )* + /// Line and column of the @font-face rule source code. + pub source_location: SourceLocation, } impl FontFaceRuleData { @@ -194,6 +199,10 @@ macro_rules! font_face_descriptors_common { $( $ident: None, )* + source_location: SourceLocation { + line: 0, + column: 0, + }, } } diff --git a/servo/components/style/gecko/generated/bindings.rs b/servo/components/style/gecko/generated/bindings.rs index bf386498df91..00e623b931fc 100644 --- a/servo/components/style/gecko/generated/bindings.rs +++ b/servo/components/style/gecko/generated/bindings.rs @@ -1255,7 +1255,8 @@ extern "C" { -> *const ::std::os::raw::c_char; } extern "C" { - pub fn Gecko_CSSFontFaceRule_Create() -> *mut nsCSSFontFaceRule; + pub fn Gecko_CSSFontFaceRule_Create(line: u32, column: u32) + -> *mut nsCSSFontFaceRule; } extern "C" { pub fn Gecko_CSSFontFaceRule_GetCssText(rule: *const nsCSSFontFaceRule, @@ -1621,7 +1622,8 @@ extern "C" { media_list: *const RawServoMediaList, extra_data: - *mut RawGeckoURLExtraData) + *mut RawGeckoURLExtraData, + line_number_offset: u32) -> RawServoStyleSheetStrong; } extern "C" { @@ -1636,7 +1638,8 @@ extern "C" { *mut ServoStyleSheet, data: *const nsACString, extra_data: - *mut RawGeckoURLExtraData); + *mut RawGeckoURLExtraData, + line_number_offset: u32); } extern "C" { pub fn Servo_StyleSheet_HasRules(sheet: RawServoStyleSheetBorrowed) @@ -1726,7 +1729,8 @@ extern "C" { } extern "C" { pub fn Servo_CssRules_GetStyleRuleAt(rules: ServoCssRulesBorrowed, - index: u32) + index: u32, line: *mut u32, + column: *mut u32) -> RawServoStyleRuleStrong; } extern "C" { @@ -1739,7 +1743,8 @@ extern "C" { } extern "C" { pub fn Servo_CssRules_GetMediaRuleAt(rules: ServoCssRulesBorrowed, - index: u32) + index: u32, line: *mut u32, + column: *mut u32) -> RawServoMediaRuleStrong; } extern "C" { @@ -1756,7 +1761,8 @@ extern "C" { } extern "C" { pub fn Servo_CssRules_GetNamespaceRuleAt(rules: ServoCssRulesBorrowed, - index: u32) + index: u32, line: *mut u32, + column: *mut u32) -> RawServoNamespaceRuleStrong; } extern "C" { @@ -1769,7 +1775,9 @@ extern "C" { } extern "C" { pub fn Servo_CssRules_GetPageRuleAt(rules: ServoCssRulesBorrowed, - index: u32) -> RawServoPageRuleStrong; + index: u32, line: *mut u32, + column: *mut u32) + -> RawServoPageRuleStrong; } extern "C" { pub fn Servo_PageRule_Debug(rule: RawServoPageRuleBorrowed, @@ -1781,7 +1789,8 @@ extern "C" { } extern "C" { pub fn Servo_CssRules_GetSupportsRuleAt(rules: ServoCssRulesBorrowed, - index: u32) + index: u32, line: *mut u32, + column: *mut u32) -> RawServoSupportsRuleStrong; } extern "C" { @@ -1798,7 +1807,8 @@ extern "C" { } extern "C" { pub fn Servo_CssRules_GetDocumentRuleAt(rules: ServoCssRulesBorrowed, - index: u32) + index: u32, line: *mut u32, + column: *mut u32) -> RawServoDocumentRuleStrong; } extern "C" { diff --git a/servo/components/style/gecko/rules.rs b/servo/components/style/gecko/rules.rs index d2d78de238a9..7af5fc599b75 100644 --- a/servo/components/style/gecko/rules.rs +++ b/servo/components/style/gecko/rules.rs @@ -117,7 +117,9 @@ impl ToNsCssValue for Vec { impl From for FontFaceRule { fn from(data: FontFaceRuleData) -> FontFaceRule { let mut result = unsafe { - UniqueRefPtr::from_addrefed(bindings::Gecko_CSSFontFaceRule_Create()) + UniqueRefPtr::from_addrefed(bindings::Gecko_CSSFontFaceRule_Create( + data.source_location.line as u32, data.source_location.column as u32 + )) }; data.set_descriptors(&mut result.mDecl.mDescriptors); result.get() diff --git a/servo/components/style/stylesheets.rs b/servo/components/style/stylesheets.rs index 84b5753fb9bf..98c139481218 100644 --- a/servo/components/style/stylesheets.rs +++ b/servo/components/style/stylesheets.rs @@ -10,7 +10,7 @@ use {Atom, Prefix, Namespace}; use context::QuirksMode; use counter_style::{CounterStyleRule, parse_counter_style_name, parse_counter_style_body}; use cssparser::{AtRuleParser, Parser, QualifiedRuleParser}; -use cssparser::{AtRuleType, RuleListParser, parse_one_rule}; +use cssparser::{AtRuleType, RuleListParser, parse_one_rule, SourceLocation}; use cssparser::ToCss as ParserToCss; use document_condition::DocumentCondition; use error_reporting::{ParseErrorReporter, NullReporter}; @@ -489,12 +489,22 @@ impl ToCssWithGuard for CssRule { } } +/// Calculates the location of a rule's source given an offset. +fn get_location_with_offset(location: SourceLocation, offset: u64) + -> SourceLocation { + SourceLocation { + line: location.line + offset as usize - 1, + column: location.column, + } +} + #[derive(Debug, PartialEq)] #[allow(missing_docs)] pub struct NamespaceRule { /// `None` for the default Namespace pub prefix: Option, pub url: Namespace, + pub source_location: SourceLocation, } impl ToCssWithGuard for NamespaceRule { @@ -581,6 +591,7 @@ impl ToCssWithGuard for KeyframesRule { pub struct MediaRule { pub media_queries: Arc>, pub rules: Arc>, + pub source_location: SourceLocation, } impl ToCssWithGuard for MediaRule { @@ -609,6 +620,8 @@ pub struct SupportsRule { pub rules: Arc>, /// The result of evaluating the condition pub enabled: bool, + /// The line and column of the rule's source code. + pub source_location: SourceLocation, } impl ToCssWithGuard for SupportsRule { @@ -630,15 +643,19 @@ impl ToCssWithGuard for SupportsRule { /// /// [page]: https://drafts.csswg.org/css2/page.html#page-box /// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors +#[allow(missing_docs)] #[derive(Debug)] -pub struct PageRule(pub Arc>); +pub struct PageRule { + pub block: Arc>, + pub source_location: SourceLocation, +} impl ToCssWithGuard for PageRule { // Serialization of PageRule is not specced, adapted from steps for StyleRule. fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result where W: fmt::Write { dest.write_str("@page { ")?; - let declaration_block = self.0.read_with(guard); + let declaration_block = self.block.read_with(guard); declaration_block.to_css(dest)?; if declaration_block.declarations().len() > 0 { write!(dest, " ")?; @@ -652,6 +669,7 @@ impl ToCssWithGuard for PageRule { pub struct StyleRule { pub selectors: SelectorList, pub block: Arc>, + pub source_location: SourceLocation, } impl ToCssWithGuard for StyleRule { @@ -686,6 +704,8 @@ pub struct DocumentRule { pub condition: DocumentCondition, /// Child rules pub rules: Arc>, + /// The line and column of the rule's source code. + pub source_location: SourceLocation, } impl ToCssWithGuard for DocumentRule { @@ -708,15 +728,15 @@ impl Stylesheet { css: &str, url_data: &UrlExtraData, stylesheet_loader: Option<&StylesheetLoader>, - error_reporter: &ParseErrorReporter) { + error_reporter: &ParseErrorReporter, + line_number_offset: u64) { let mut namespaces = Namespaces::default(); // FIXME: we really should update existing.url_data with the given url_data, // otherwise newly inserted rule may not have the right base url. let (rules, dirty_on_viewport_size_change) = Stylesheet::parse_rules( css, url_data, existing.origin, &mut namespaces, &existing.shared_lock, stylesheet_loader, error_reporter, - existing.quirks_mode, 0u64); - + existing.quirks_mode, line_number_offset); *existing.namespaces.write() = namespaces; existing.dirty_on_viewport_size_change .store(dirty_on_viewport_size_change, Ordering::Release); @@ -996,21 +1016,21 @@ pub enum VendorPrefix { enum AtRulePrelude { /// A @font-face rule prelude. - FontFace, + FontFace(SourceLocation), /// A @counter-style rule prelude, with its counter style name. CounterStyle(CustomIdent), /// A @media rule prelude, with its media queries. - Media(Arc>), + Media(Arc>, SourceLocation), /// An @supports rule, with its conditional - Supports(SupportsCondition), + Supports(SupportsCondition, SourceLocation), /// A @viewport rule prelude. Viewport, /// A @keyframes rule, with its animation name and vendor prefix if exists. Keyframes(KeyframesName, Option), /// A @page rule prelude. - Page, + Page(SourceLocation), /// A @document rule, with its conditional. - Document(DocumentCondition), + Document(DocumentCondition, SourceLocation), } @@ -1066,6 +1086,9 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> { if self.state.get() <= State::Namespaces { self.state.set(State::Namespaces); + let location = get_location_with_offset(input.current_source_location(), + self.context.line_number_offset); + let prefix_result = input.try(|input| input.expect_ident()); let url = Namespace::from(try!(input.expect_url_or_string())); @@ -1082,6 +1105,7 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> { self.shared_lock.wrap(NamespaceRule { prefix: opt_prefix, url: url, + source_location: location, }) )))) } else { @@ -1176,18 +1200,20 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> { fn parse_prelude(&mut self, name: &str, input: &mut Parser) -> Result, ()> { + let location = get_location_with_offset(input.current_source_location(), + self.context.line_number_offset); match_ignore_ascii_case! { name, "media" => { let media_queries = parse_media_query_list(self.context, input); let arc = Arc::new(self.shared_lock.wrap(media_queries)); - Ok(AtRuleType::WithBlock(AtRulePrelude::Media(arc))) + Ok(AtRuleType::WithBlock(AtRulePrelude::Media(arc, location))) }, "supports" => { let cond = SupportsCondition::parse(input)?; - Ok(AtRuleType::WithBlock(AtRulePrelude::Supports(cond))) + Ok(AtRuleType::WithBlock(AtRulePrelude::Supports(cond, location))) }, "font-face" => { - Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace)) + Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace(location))) }, "counter-style" => { if !cfg!(feature = "gecko") { @@ -1229,7 +1255,7 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> { }, "page" => { if cfg!(feature = "gecko") { - Ok(AtRuleType::WithBlock(AtRulePrelude::Page)) + Ok(AtRuleType::WithBlock(AtRulePrelude::Page(location))) } else { Err(()) } @@ -1237,7 +1263,7 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> { "-moz-document" => { if cfg!(feature = "gecko") { let cond = DocumentCondition::parse(self.context, input)?; - Ok(AtRuleType::WithBlock(AtRulePrelude::Document(cond))) + Ok(AtRuleType::WithBlock(AtRulePrelude::Document(cond, location))) } else { Err(()) } @@ -1248,28 +1274,30 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> { fn parse_block(&mut self, prelude: AtRulePrelude, input: &mut Parser) -> Result { match prelude { - AtRulePrelude::FontFace => { + AtRulePrelude::FontFace(location) => { let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::FontFace)); Ok(CssRule::FontFace(Arc::new(self.shared_lock.wrap( - parse_font_face_block(&context, input).into())))) + parse_font_face_block(&context, input, location).into())))) } AtRulePrelude::CounterStyle(name) => { let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::CounterStyle)); Ok(CssRule::CounterStyle(Arc::new(self.shared_lock.wrap( parse_counter_style_body(name, &context, input)?)))) } - AtRulePrelude::Media(media_queries) => { + AtRulePrelude::Media(media_queries, location) => { Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule { media_queries: media_queries, rules: self.parse_nested_rules(input, CssRuleType::Media), + source_location: location, })))) } - AtRulePrelude::Supports(cond) => { + AtRulePrelude::Supports(cond, location) => { let enabled = cond.eval(self.context); Ok(CssRule::Supports(Arc::new(self.shared_lock.wrap(SupportsRule { condition: cond, rules: self.parse_nested_rules(input, CssRuleType::Supports), enabled: enabled, + source_location: location, })))) } AtRulePrelude::Viewport => { @@ -1285,18 +1313,20 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> { vendor_prefix: prefix, })))) } - AtRulePrelude::Page => { + AtRulePrelude::Page(location) => { let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Page)); let declarations = parse_property_declaration_list(&context, input); - Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule( - Arc::new(self.shared_lock.wrap(declarations)) - ))))) + Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule { + block: Arc::new(self.shared_lock.wrap(declarations)), + source_location: location, + })))) } - AtRulePrelude::Document(cond) => { + AtRulePrelude::Document(cond, location) => { if cfg!(feature = "gecko") { Ok(CssRule::Document(Arc::new(self.shared_lock.wrap(DocumentRule { condition: cond, rules: self.parse_nested_rules(input, CssRuleType::Document), + source_location: location, })))) } else { unreachable!() @@ -1320,11 +1350,14 @@ impl<'a, 'b> QualifiedRuleParser for NestedRuleParser<'a, 'b> { fn parse_block(&mut self, prelude: SelectorList, input: &mut Parser) -> Result { + let location = get_location_with_offset(input.current_source_location(), + self.context.line_number_offset); let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Style)); let declarations = parse_property_declaration_list(&context, input); Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule { selectors: prelude, - block: Arc::new(self.shared_lock.wrap(declarations)) + block: Arc::new(self.shared_lock.wrap(declarations)), + source_location: location, })))) } } diff --git a/servo/ports/geckolib/glue.rs b/servo/ports/geckolib/glue.rs index f3c6b680f415..eb70d86eb0e3 100644 --- a/servo/ports/geckolib/glue.rs +++ b/servo/ports/geckolib/glue.rs @@ -571,7 +571,8 @@ pub extern "C" fn Servo_StyleSheet_FromUTF8Bytes(loader: *mut Loader, data: *const nsACString, mode: SheetParsingMode, media_list: *const RawServoMediaList, - extra_data: *mut URLExtraData) + extra_data: *mut URLExtraData, + line_number_offset: u32) -> RawServoStyleSheetStrong { let global_style_data = &*GLOBAL_STYLE_DATA; let input = unsafe { data.as_ref().unwrap().as_str_unchecked() }; @@ -605,7 +606,8 @@ pub extern "C" fn Servo_StyleSheet_FromUTF8Bytes(loader: *mut Loader, Arc::new(Stylesheet::from_str( input, url_data.clone(), origin, media, - shared_lock, loader, &RustLogReporter, QuirksMode::NoQuirks, 0u64) + shared_lock, loader, &RustLogReporter, + QuirksMode::NoQuirks, line_number_offset as u64) ).into_strong() } @@ -614,7 +616,8 @@ pub extern "C" fn Servo_StyleSheet_ClearAndUpdate(stylesheet: RawServoStyleSheet loader: *mut Loader, gecko_stylesheet: *mut ServoStyleSheet, data: *const nsACString, - extra_data: *mut URLExtraData) + extra_data: *mut URLExtraData, + line_number_offset: u32) { let input = unsafe { data.as_ref().unwrap().as_str_unchecked() }; let url_data = unsafe { RefPtr::from_ptr_ref(&extra_data) }; @@ -632,8 +635,8 @@ pub extern "C" fn Servo_StyleSheet_ClearAndUpdate(stylesheet: RawServoStyleSheet }; let sheet = Stylesheet::as_arc(&stylesheet); - Stylesheet::update_from_str(&sheet, input, url_data, - loader, &RustLogReporter); + Stylesheet::update_from_str(&sheet, input, url_data, loader, + &RustLogReporter, line_number_offset as u64); } #[no_mangle] @@ -782,16 +785,24 @@ macro_rules! impl_basic_rule_funcs { to_css: $to_css:ident, } => { #[no_mangle] - pub extern "C" fn $getter(rules: ServoCssRulesBorrowed, index: u32) -> Strong<$raw_type> { - read_locked_arc(rules, |rules: &CssRules| { - match rules.0[index as usize] { - CssRule::$name(ref rule) => rule.clone().into_strong(), - _ => { - unreachable!(concat!(stringify!($getter), "should only be called ", - "on a ", stringify!($name), " rule")); - } + pub extern "C" fn $getter(rules: ServoCssRulesBorrowed, index: u32, + line: *mut u32, column: *mut u32) + -> Strong<$raw_type> { + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); + let rules = Locked::::as_arc(&rules).read_with(&guard); + match rules.0[index as usize] { + CssRule::$name(ref rule) => { + let location = rule.read_with(&guard).source_location; + *unsafe { line.as_mut().unwrap() } = location.line as u32; + *unsafe { column.as_mut().unwrap() } = location.column as u32; + rule.clone().into_strong() + }, + _ => { + unreachable!(concat!(stringify!($getter), "should only be called ", + "on a ", stringify!($name), " rule")); } - }) + } } #[no_mangle] @@ -924,7 +935,7 @@ pub extern "C" fn Servo_NamespaceRule_GetURI(rule: RawServoNamespaceRuleBorrowed #[no_mangle] pub extern "C" fn Servo_PageRule_GetStyle(rule: RawServoPageRuleBorrowed) -> RawServoDeclarationBlockStrong { read_locked_arc(rule, |rule: &PageRule| { - rule.0.clone().into_strong() + rule.block.clone().into_strong() }) } @@ -933,7 +944,7 @@ pub extern "C" fn Servo_PageRule_SetStyle(rule: RawServoPageRuleBorrowed, declarations: RawServoDeclarationBlockBorrowed) { let declarations = Locked::::as_arc(&declarations); write_locked_arc(rule, |rule: &mut PageRule| { - rule.0 = declarations.clone(); + rule.block = declarations.clone(); }) } diff --git a/servo/tests/unit/gfx/Cargo.toml b/servo/tests/unit/gfx/Cargo.toml index f6a5c4605b6b..6f3961599bcd 100644 --- a/servo/tests/unit/gfx/Cargo.toml +++ b/servo/tests/unit/gfx/Cargo.toml @@ -10,6 +10,7 @@ path = "lib.rs" doctest = false [dependencies] +cssparser = "0.13.3" gfx = {path = "../../../components/gfx"} ipc-channel = "0.7" style = {path = "../../../components/style"} diff --git a/servo/tests/unit/gfx/font_cache_thread.rs b/servo/tests/unit/gfx/font_cache_thread.rs index 0ff179fab1ee..91e5b712dc71 100644 --- a/servo/tests/unit/gfx/font_cache_thread.rs +++ b/servo/tests/unit/gfx/font_cache_thread.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use cssparser::SourceLocation; use gfx::font_cache_thread::FontCacheThread; use ipc_channel::ipc; use style::computed_values::font_family::FamilyName; @@ -23,6 +24,10 @@ fn test_local_web_font() { let font_face_rule = FontFaceRuleData { family: Some(family_name.clone()), sources: Some(vec![Source::Local(variant_name)]), + source_location: SourceLocation { + line: 0, + column: 0, + }, }; font_cache_thread.add_web_font( diff --git a/servo/tests/unit/gfx/lib.rs b/servo/tests/unit/gfx/lib.rs index 1521566c5c52..a9b45bd93e8c 100644 --- a/servo/tests/unit/gfx/lib.rs +++ b/servo/tests/unit/gfx/lib.rs @@ -2,6 +2,7 @@ * 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/. */ +extern crate cssparser; extern crate gfx; extern crate ipc_channel; extern crate style; diff --git a/servo/tests/unit/style/stylesheets.rs b/servo/tests/unit/style/stylesheets.rs index 64bbcf1874f0..1cef0f377bee 100644 --- a/servo/tests/unit/style/stylesheets.rs +++ b/servo/tests/unit/style/stylesheets.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use cssparser::{self, Parser as CssParser, SourcePosition}; +use cssparser::{self, Parser as CssParser, SourcePosition, SourceLocation}; use html5ever::{Namespace as NsAtom}; use media_queries::CSSErrorReporterTest; use parking_lot::RwLock; @@ -82,7 +82,11 @@ fn test_parse_stylesheet() { rules: CssRules::new(vec![ CssRule::Namespace(Arc::new(stylesheet.shared_lock.wrap(NamespaceRule { prefix: None, - url: NsAtom::from("http://www.w3.org/1999/xhtml") + url: NsAtom::from("http://www.w3.org/1999/xhtml"), + source_location: SourceLocation { + line: 1, + column: 19, + }, }))), CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { selectors: SelectorList(vec![ @@ -116,6 +120,10 @@ fn test_parse_stylesheet() { DeclaredValueOwned::CSSWideKeyword(CSSWideKeyword::Inherit)), Importance::Important), ]))), + source_location: SourceLocation { + line: 3, + column: 31, + }, }))), CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { selectors: SelectorList(vec![ @@ -152,6 +160,10 @@ fn test_parse_stylesheet() { (PropertyDeclaration::Display(longhands::display::SpecifiedValue::block), Importance::Normal), ]))), + source_location: SourceLocation { + line: 11, + column: 27, + }, }))), CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { selectors: SelectorList(vec![ @@ -220,6 +232,10 @@ fn test_parse_stylesheet() { ::get_initial_specified_value()])), Importance::Normal), ]))), + source_location: SourceLocation { + line: 15, + column: 20, + }, }))), CssRule::Keyframes(Arc::new(stylesheet.shared_lock.wrap(KeyframesRule { name: KeyframesName::Ident(CustomIdent("foo".into())), diff --git a/servo/tests/unit/style/stylist.rs b/servo/tests/unit/style/stylist.rs index baba229865a7..e50ff98845e4 100644 --- a/servo/tests/unit/style/stylist.rs +++ b/servo/tests/unit/style/stylist.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use cssparser::SourceLocation; use html5ever::LocalName; use selectors::parser::LocalName as LocalNameSelector; use selectors::parser::Selector; @@ -32,6 +33,10 @@ fn get_mock_rules(css_selectors: &[&str]) -> (Vec>, SharedRwLock) { longhands::display::SpecifiedValue::block), Importance::Normal ))), + source_location: SourceLocation { + line: 0, + column: 0, + }, })); let guard = shared_lock.read(); From be99777e749054af21e6710106e36ed611c63cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Fri, 12 May 2017 19:52:04 +0200 Subject: [PATCH 12/30] Bug 1364516 - Move gCustomizeMode.setTab call out of updateTabLabelAndIcon. r=mikedeboer While setTab does end up setting the label and icon, it also does completely different and more crucial things. This call really doesn't belong in updateTabLabelAndIcon. MozReview-Commit-ID: 9HXYS0fXgRN --HG-- extra : rebase_source : f11e283bdc487f8df4ba8013f420158b6f356dbe --- browser/components/sessionstore/SessionStore.jsm | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index 0b8d39fc03b0..9ba9742c432e 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -2618,9 +2618,7 @@ var SessionStoreInternal = { /** * Updates the label and icon for a using the data from - * tabData. If the tab being updated happens to be the - * customization mode tab, this function will tell the window's - * CustomizeMode instance about it. + * tabData. * * @param tab * The to update. @@ -2629,6 +2627,10 @@ var SessionStoreInternal = { * not supplied, the data will be retrieved from the cache. */ updateTabLabelAndIcon(tab, tabData = null) { + if (tab.hasAttribute("customizemode")) { + return; + } + let browser = tab.linkedBrowser; let win = browser.ownerGlobal; @@ -2648,8 +2650,6 @@ var SessionStoreInternal = { } else if (activePageData.url != "about:blank") { win.gBrowser.setInitialTabTitle(tab, activePageData.url); } - } else if (tab.hasAttribute("customizemode")) { - win.gCustomizeMode.setTab(tab); } // Restore the tab icon. @@ -3692,6 +3692,10 @@ var SessionStoreInternal = { }; } + if (tab.hasAttribute("customizemode")) { + win.gCustomizeMode.setTab(tab); + } + // Update tab label and icon to show something // while we wait for the messages to be processed. this.updateTabLabelAndIcon(tab, tabData); From 50301fae547950a6bf0abc0a791a06aad4d25138 Mon Sep 17 00:00:00 2001 From: Fernando Jimenez Moreno Date: Tue, 25 Apr 2017 21:01:45 +0200 Subject: [PATCH 13/30] Bug 1350175 - Part 1: Set stylesheet line offset. r=xidorn MozReview-Commit-ID: 49B3bZaWjWc --HG-- extra : rebase_source : 005986f47fd243d7faddffa31689eff50a7161e5 --- layout/style/ServoBindingList.h | 6 ++++-- layout/style/ServoStyleSheet.cpp | 5 +++-- layout/style/test/gtest/StyloParsingBench.cpp | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h index e3d082724595..1c3f84b3e86c 100644 --- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -30,7 +30,8 @@ SERVO_BINDING_FUNC(Servo_StyleSheet_FromUTF8Bytes, RawServoStyleSheetStrong, const nsACString* data, mozilla::css::SheetParsingMode parsing_mode, const RawServoMediaList* media_list, - RawGeckoURLExtraData* extra_data) + RawGeckoURLExtraData* extra_data, + uint32_t line_number_offset) SERVO_BINDING_FUNC(Servo_ImportRule_GetSheet, RawServoStyleSheetStrong, const RawServoImportRuleBorrowed import_rule) @@ -40,7 +41,8 @@ SERVO_BINDING_FUNC(Servo_StyleSheet_ClearAndUpdate, mozilla::css::Loader* loader, mozilla::ServoStyleSheet* gecko_stylesheet, const nsACString* data, - RawGeckoURLExtraData* extra_data) + RawGeckoURLExtraData* extra_data, + uint32_t line_number_offset) SERVO_BINDING_FUNC(Servo_StyleSheet_HasRules, bool, RawServoStyleSheetBorrowed sheet) SERVO_BINDING_FUNC(Servo_StyleSheet_GetRules, ServoCssRulesStrong, diff --git a/layout/style/ServoStyleSheet.cpp b/layout/style/ServoStyleSheet.cpp index 624f02981fbb..8e76ddbd4b59 100644 --- a/layout/style/ServoStyleSheet.cpp +++ b/layout/style/ServoStyleSheet.cpp @@ -110,13 +110,14 @@ ServoStyleSheet::ParseSheet(css::Loader* aLoader, Inner()->mSheet = Servo_StyleSheet_FromUTF8Bytes( - aLoader, this, &input, mParsingMode, media, extraData).Consume(); + aLoader, this, &input, mParsingMode, media, extraData, aLineNumber + ).Consume(); } else { // TODO(emilio): Once we have proper inner cloning (which we don't right // now) we should update the mediaList here too, though it's slightly // tricky. Servo_StyleSheet_ClearAndUpdate(Inner()->mSheet, aLoader, - this, &input, extraData); + this, &input, extraData, aLineNumber); } Inner()->mURLData = extraData.forget(); diff --git a/layout/style/test/gtest/StyloParsingBench.cpp b/layout/style/test/gtest/StyloParsingBench.cpp index 3e93ebb6207b..985b7dbffeff 100644 --- a/layout/style/test/gtest/StyloParsingBench.cpp +++ b/layout/style/test/gtest/StyloParsingBench.cpp @@ -28,7 +28,7 @@ static void ServoParsingBench() { NullPrincipalURI::Create(), nullptr, NullPrincipal::Create()); for (int i = 0; i < PARSING_REPETITIONS; i++) { RefPtr stylesheet = Servo_StyleSheet_FromUTF8Bytes( - nullptr, nullptr, &css, eAuthorSheetFeatures, nullptr, data + nullptr, nullptr, &css, eAuthorSheetFeatures, nullptr, data, 0 ).Consume(); } } From 9e66b37474f8d8bbece7fda52349b674d6e2eeb4 Mon Sep 17 00:00:00 2001 From: Fernando Jimenez Moreno Date: Tue, 25 Apr 2017 16:24:39 +0200 Subject: [PATCH 14/30] Bug 1350175 - Part 2: FFI changes to get line and column. r=xidorn MozReview-Commit-ID: 87pVnoLmjPl --HG-- extra : rebase_source : 1decaf9bc3aa75d64254aea8fdb08679c2557ca2 --- layout/style/ServoBindingList.h | 3 ++- layout/style/ServoCSSRuleList.cpp | 8 ++++++-- layout/style/ServoDocumentRule.cpp | 3 ++- layout/style/ServoDocumentRule.h | 3 ++- layout/style/ServoMediaRule.cpp | 3 ++- layout/style/ServoMediaRule.h | 3 ++- layout/style/ServoNamespaceRule.h | 5 +++-- layout/style/ServoPageRule.cpp | 5 +++-- layout/style/ServoPageRule.h | 3 ++- layout/style/ServoStyleRule.cpp | 5 +++-- layout/style/ServoStyleRule.h | 3 ++- layout/style/ServoSupportsRule.cpp | 3 ++- layout/style/ServoSupportsRule.h | 3 ++- 13 files changed, 33 insertions(+), 17 deletions(-) diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h index 1c3f84b3e86c..8c94574a6d91 100644 --- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -102,7 +102,8 @@ SERVO_BINDING_FUNC(Servo_CssRules_DeleteRule, nsresult, #define BASIC_RULE_FUNCS(type_) \ SERVO_BINDING_FUNC(Servo_CssRules_Get##type_##RuleAt, \ RawServo##type_##RuleStrong, \ - ServoCssRulesBorrowed rules, uint32_t index) \ + ServoCssRulesBorrowed rules, uint32_t index, \ + uint32_t* line, uint32_t* column) \ SERVO_BINDING_FUNC(Servo_##type_##Rule_Debug, void, \ RawServo##type_##RuleBorrowed rule, nsACString* result) \ SERVO_BINDING_FUNC(Servo_##type_##Rule_GetCssText, void, \ diff --git a/layout/style/ServoCSSRuleList.cpp b/layout/style/ServoCSSRuleList.cpp index 172ef9946281..9b7d9c00375b 100644 --- a/layout/style/ServoCSSRuleList.cpp +++ b/layout/style/ServoCSSRuleList.cpp @@ -84,8 +84,12 @@ ServoCSSRuleList::GetRule(uint32_t aIndex) switch (rule) { #define CASE_RULE(const_, name_) \ case nsIDOMCSSRule::const_##_RULE: { \ - ruleObj = new Servo##name_##Rule( \ - Servo_CssRules_Get##name_##RuleAt(mRawRules, aIndex).Consume()); \ + uint32_t line = 0, column = 0; \ + RefPtr rule = \ + Servo_CssRules_Get##name_##RuleAt( \ + mRawRules, aIndex, &line, &column \ + ).Consume(); \ + ruleObj = new Servo##name_##Rule(rule.forget(), line, column); \ break; \ } CASE_RULE(STYLE, Style) diff --git a/layout/style/ServoDocumentRule.cpp b/layout/style/ServoDocumentRule.cpp index 38eb2d212acc..c79daace5c44 100644 --- a/layout/style/ServoDocumentRule.cpp +++ b/layout/style/ServoDocumentRule.cpp @@ -13,7 +13,8 @@ using namespace mozilla::dom; namespace mozilla { -ServoDocumentRule::ServoDocumentRule(RefPtr aRawRule) +ServoDocumentRule::ServoDocumentRule(RefPtr aRawRule, + uint32_t aLine, uint32_t aColumn) : CSSMozDocumentRule(Servo_DocumentRule_GetRules(aRawRule).Consume()) , mRawRule(Move(aRawRule)) { diff --git a/layout/style/ServoDocumentRule.h b/layout/style/ServoDocumentRule.h index 151401cc1832..1c2d0849e903 100644 --- a/layout/style/ServoDocumentRule.h +++ b/layout/style/ServoDocumentRule.h @@ -17,7 +17,8 @@ namespace mozilla { class ServoDocumentRule final : public dom::CSSMozDocumentRule { public: - explicit ServoDocumentRule(RefPtr aRawRule); + ServoDocumentRule(RefPtr aRawRule, + uint32_t aLine, uint32_t aColumn); NS_DECL_ISUPPORTS_INHERITED diff --git a/layout/style/ServoMediaRule.cpp b/layout/style/ServoMediaRule.cpp index 408a1044f14c..b476315d510b 100644 --- a/layout/style/ServoMediaRule.cpp +++ b/layout/style/ServoMediaRule.cpp @@ -15,7 +15,8 @@ using namespace mozilla::dom; namespace mozilla { -ServoMediaRule::ServoMediaRule(RefPtr aRawRule) +ServoMediaRule::ServoMediaRule(RefPtr aRawRule, + uint32_t aLine, uint32_t aColumn) : CSSMediaRule(Servo_MediaRule_GetRules(aRawRule).Consume()) , mRawRule(Move(aRawRule)) { diff --git a/layout/style/ServoMediaRule.h b/layout/style/ServoMediaRule.h index 93bd9cf82ca8..f57ac790661b 100644 --- a/layout/style/ServoMediaRule.h +++ b/layout/style/ServoMediaRule.h @@ -19,7 +19,8 @@ class ServoMediaList; class ServoMediaRule final : public dom::CSSMediaRule { public: - explicit ServoMediaRule(RefPtr aRawRule); + ServoMediaRule(RefPtr aRawRule, + uint32_t aLine, uint32_t aColumn); NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServoMediaRule, dom::CSSMediaRule) diff --git a/layout/style/ServoNamespaceRule.h b/layout/style/ServoNamespaceRule.h index eec18d9483d5..eb9da5ef8443 100644 --- a/layout/style/ServoNamespaceRule.h +++ b/layout/style/ServoNamespaceRule.h @@ -15,8 +15,9 @@ namespace mozilla { class ServoNamespaceRule : public dom::CSSNamespaceRule { public: - explicit ServoNamespaceRule(already_AddRefed aRule) - : CSSNamespaceRule(0, 0) + ServoNamespaceRule(already_AddRefed aRule, + uint32_t aLine, uint32_t aColumn) + : CSSNamespaceRule(aLine, aColumn) , mRawRule(Move(aRule)) { } diff --git a/layout/style/ServoPageRule.cpp b/layout/style/ServoPageRule.cpp index 0d5957e96919..a18d769a06ee 100644 --- a/layout/style/ServoPageRule.cpp +++ b/layout/style/ServoPageRule.cpp @@ -104,8 +104,9 @@ ServoPageRuleDeclaration::GetServoCSSParsingEnvironment() const // -- ServoPageRule -------------------------------------------------- -ServoPageRule::ServoPageRule(RefPtr aRawRule) - : CSSPageRule(0, 0) +ServoPageRule::ServoPageRule(RefPtr aRawRule, + uint32_t aLine, uint32_t aColumn) + : CSSPageRule(aLine, aColumn) , mRawRule(Move(aRawRule)) , mDecls(Servo_PageRule_GetStyle(mRawRule).Consume()) { diff --git a/layout/style/ServoPageRule.h b/layout/style/ServoPageRule.h index c47094b9109e..530db84442b6 100644 --- a/layout/style/ServoPageRule.h +++ b/layout/style/ServoPageRule.h @@ -51,7 +51,8 @@ private: class ServoPageRule final : public dom::CSSPageRule { public: - explicit ServoPageRule(RefPtr aRawRule); + ServoPageRule(RefPtr aRawRule, + uint32_t aLine, uint32_t aColumn); NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED( diff --git a/layout/style/ServoStyleRule.cpp b/layout/style/ServoStyleRule.cpp index 285a4647b5e2..27a3eeb405dc 100644 --- a/layout/style/ServoStyleRule.cpp +++ b/layout/style/ServoStyleRule.cpp @@ -109,8 +109,9 @@ ServoStyleRuleDeclaration::GetServoCSSParsingEnvironment() const // -- ServoStyleRule -------------------------------------------------- -ServoStyleRule::ServoStyleRule(already_AddRefed aRawRule) - : BindingStyleRule(0, 0) +ServoStyleRule::ServoStyleRule(already_AddRefed aRawRule, + uint32_t aLine, uint32_t aColumn) + : BindingStyleRule(aLine, aColumn) , mRawRule(aRawRule) , mDecls(Servo_StyleRule_GetStyle(mRawRule).Consume()) { diff --git a/layout/style/ServoStyleRule.h b/layout/style/ServoStyleRule.h index b45980271eda..ba94121d6cfb 100644 --- a/layout/style/ServoStyleRule.h +++ b/layout/style/ServoStyleRule.h @@ -53,7 +53,8 @@ class ServoStyleRule final : public BindingStyleRule , public nsIDOMCSSStyleRule { public: - explicit ServoStyleRule(already_AddRefed aRawRule); + ServoStyleRule(already_AddRefed aRawRule, + uint32_t aLine, uint32_t aColumn); NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ServoStyleRule, diff --git a/layout/style/ServoSupportsRule.cpp b/layout/style/ServoSupportsRule.cpp index 4735eac14974..5a035d393bcd 100644 --- a/layout/style/ServoSupportsRule.cpp +++ b/layout/style/ServoSupportsRule.cpp @@ -14,7 +14,8 @@ using namespace mozilla::dom; namespace mozilla { -ServoSupportsRule::ServoSupportsRule(RefPtr aRawRule) +ServoSupportsRule::ServoSupportsRule(RefPtr aRawRule, + uint32_t aLine, uint32_t aColumn) : CSSSupportsRule(Servo_SupportsRule_GetRules(aRawRule).Consume()) , mRawRule(Move(aRawRule)) { diff --git a/layout/style/ServoSupportsRule.h b/layout/style/ServoSupportsRule.h index ff3ec76296f3..10c7bc72009a 100644 --- a/layout/style/ServoSupportsRule.h +++ b/layout/style/ServoSupportsRule.h @@ -17,7 +17,8 @@ namespace mozilla { class ServoSupportsRule final : public dom::CSSSupportsRule { public: - explicit ServoSupportsRule(RefPtr aRawRule); + ServoSupportsRule(RefPtr aRawRule, + uint32_t aLine, uint32_t aColumn); NS_DECL_ISUPPORTS_INHERITED From 727a9d6d273d4bad8e18e87c8c3174a3b666a779 Mon Sep 17 00:00:00 2001 From: Fernando Jimenez Moreno Date: Mon, 15 May 2017 14:02:13 +0200 Subject: [PATCH 15/30] Bug 1350175 - Part 3: Set source position for the different rules. r=SimonSapin,xidorn MozReview-Commit-ID: 6AGNOLqoNHx --HG-- extra : rebase_source : 476d440c18e98e3fa690fd064bf42483e63ae057 --- layout/style/ServoBindings.cpp | 4 ++-- layout/style/ServoBindings.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp index 9bec8c23deee..36a9b384e377 100644 --- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -2095,9 +2095,9 @@ Gecko_CSSKeywordString(nsCSSKeyword aKeyword, uint32_t* aLength) } nsCSSFontFaceRule* -Gecko_CSSFontFaceRule_Create() +Gecko_CSSFontFaceRule_Create(uint32_t aLine, uint32_t aColumn) { - RefPtr rule = new nsCSSFontFaceRule(0, 0); + RefPtr rule = new nsCSSFontFaceRule(aLine, aColumn); return rule.forget().take(); } diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index 182f4fae0a27..54ee44580ee1 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -515,7 +515,7 @@ const char* Gecko_CSSKeywordString(nsCSSKeyword keyword, uint32_t* len); // Font face rule // Creates and returns a new (already-addrefed) nsCSSFontFaceRule object. -nsCSSFontFaceRule* Gecko_CSSFontFaceRule_Create(); +nsCSSFontFaceRule* Gecko_CSSFontFaceRule_Create(uint32_t line, uint32_t column); void Gecko_CSSFontFaceRule_GetCssText(const nsCSSFontFaceRule* rule, nsAString* result); NS_DECL_FFI_REFCOUNTING(nsCSSFontFaceRule, CSSFontFaceRule); From 1b6021c0dec4709160fea8589898e2807f413149 Mon Sep 17 00:00:00 2001 From: Mike de Boer Date: Mon, 15 May 2017 15:02:32 +0200 Subject: [PATCH 16/30] Bug 1363753 - Add an option for panelmultiviews to keep the width of the mainView synchronized across all other subviews. r=Gijs MozReview-Commit-ID: ALgU1xWydWB --HG-- extra : rebase_source : 2825b93ec67b4d841df39ef0a61a9de429ceac9d --- browser/components/customizableui/PanelMultiView.jsm | 6 +++++- browser/themes/shared/customizableui/panelUI.inc.css | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/browser/components/customizableui/PanelMultiView.jsm b/browser/components/customizableui/PanelMultiView.jsm index 9a3d402ea206..e7f137bb43fb 100644 --- a/browser/components/customizableui/PanelMultiView.jsm +++ b/browser/components/customizableui/PanelMultiView.jsm @@ -382,10 +382,12 @@ this.PanelMultiView = class { let playTransition = (!!previousViewNode && previousViewNode != viewNode); let dwu, previousRect; - if (playTransition) { + if (playTransition || this.panelViews) { dwu = this._dwu; previousRect = previousViewNode.__lastKnownBoundingRect = dwu.getBoundsWithoutFlushing(previousViewNode); + if (this.panelViews && !this._mainViewWidth) + this._mainViewWidth = previousRect.width; } // Emit the ViewShowing event so that the widget definition has a chance @@ -410,6 +412,8 @@ this.PanelMultiView = class { } } viewNode.setAttribute("current", true); + if (playTransition && this.panelViews) + viewNode.style.maxWidth = viewNode.style.minWidth = this._mainViewWidth + "px"; let evt = new window.CustomEvent("ViewShowing", { bubbles: true, cancelable: true, detail }); viewNode.dispatchEvent(evt); diff --git a/browser/themes/shared/customizableui/panelUI.inc.css b/browser/themes/shared/customizableui/panelUI.inc.css index 7594c8637979..56cde703f1e9 100644 --- a/browser/themes/shared/customizableui/panelUI.inc.css +++ b/browser/themes/shared/customizableui/panelUI.inc.css @@ -335,6 +335,7 @@ panelview:not([mainview]) .toolbarbutton-text, photonpanelmultiview panelview { background: var(--arrowpanel-background); padding: 6px 0; + min-width: @menuPanelWidth@; } photonpanelmultiview panelview[title] { From c06df98c46bf4f34664163a715eeb177778fe5d8 Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Mon, 15 May 2017 15:19:07 +0200 Subject: [PATCH 17/30] Backed out changeset b96483ca2c27 (bug 1364516) for failing mochitest browser/components/customizableui/test/browser_exit_background_customize_mode.js and eslint failure at SessionStore.jsm:3696:7 | 'win' is not defined. r=backout --- browser/components/sessionstore/SessionStore.jsm | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index 9ba9742c432e..0b8d39fc03b0 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -2618,7 +2618,9 @@ var SessionStoreInternal = { /** * Updates the label and icon for a using the data from - * tabData. + * tabData. If the tab being updated happens to be the + * customization mode tab, this function will tell the window's + * CustomizeMode instance about it. * * @param tab * The to update. @@ -2627,10 +2629,6 @@ var SessionStoreInternal = { * not supplied, the data will be retrieved from the cache. */ updateTabLabelAndIcon(tab, tabData = null) { - if (tab.hasAttribute("customizemode")) { - return; - } - let browser = tab.linkedBrowser; let win = browser.ownerGlobal; @@ -2650,6 +2648,8 @@ var SessionStoreInternal = { } else if (activePageData.url != "about:blank") { win.gBrowser.setInitialTabTitle(tab, activePageData.url); } + } else if (tab.hasAttribute("customizemode")) { + win.gCustomizeMode.setTab(tab); } // Restore the tab icon. @@ -3692,10 +3692,6 @@ var SessionStoreInternal = { }; } - if (tab.hasAttribute("customizemode")) { - win.gCustomizeMode.setTab(tab); - } - // Update tab label and icon to show something // while we wait for the messages to be processed. this.updateTabLabelAndIcon(tab, tabData); From cd9bc4e4495cc85dd757f1483ada29caf453ed82 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 15 May 2017 13:32:15 +0000 Subject: [PATCH 18/30] Bug 1364887: don't run SETA on try pushes; r=jmaher MozReview-Commit-ID: 7L2I3WWziSE --HG-- extra : rebase_source : 80796238754bb6081d2bde082b6fb992e24564a5 --- taskcluster/taskgraph/transforms/tests.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/taskcluster/taskgraph/transforms/tests.py b/taskcluster/taskgraph/transforms/tests.py index fcbc48950bd8..e1e2f9c27fc7 100644 --- a/taskcluster/taskgraph/transforms/tests.py +++ b/taskcluster/taskgraph/transforms/tests.py @@ -771,7 +771,6 @@ def make_job_description(config, tests): jobdesc['run-on-projects'] = test['run-on-projects'] jobdesc['scopes'] = [] jobdesc['tags'] = test.get('tags', {}) - jobdesc['optimizations'] = [['seta']] # always run SETA for tests jobdesc['extra'] = { 'chunks': { 'current': test['this-chunk'], @@ -788,6 +787,12 @@ def make_job_description(config, tests): 'tier': test['tier'], 'platform': test.get('treeherder-machine-platform', test['build-platform']), } + + # run SETA unless this is a try push + jobdesc['optimizations'] = optimizations = [] + if config['project'] != 'try': + optimizations.append(['seta']) + run = jobdesc['run'] = {} run['using'] = 'mozharness-test' run['test'] = test From a54ca523c97c2e7fe1a323e2beb8b0e6e4fe3e07 Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Mon, 15 May 2017 15:49:32 +0200 Subject: [PATCH 19/30] Backed out changeset 38f7bc71c0de (bug 1364887) for breaking gecko decision task. r=backout --- taskcluster/taskgraph/transforms/tests.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/taskcluster/taskgraph/transforms/tests.py b/taskcluster/taskgraph/transforms/tests.py index e1e2f9c27fc7..fcbc48950bd8 100644 --- a/taskcluster/taskgraph/transforms/tests.py +++ b/taskcluster/taskgraph/transforms/tests.py @@ -771,6 +771,7 @@ def make_job_description(config, tests): jobdesc['run-on-projects'] = test['run-on-projects'] jobdesc['scopes'] = [] jobdesc['tags'] = test.get('tags', {}) + jobdesc['optimizations'] = [['seta']] # always run SETA for tests jobdesc['extra'] = { 'chunks': { 'current': test['this-chunk'], @@ -787,12 +788,6 @@ def make_job_description(config, tests): 'tier': test['tier'], 'platform': test.get('treeherder-machine-platform', test['build-platform']), } - - # run SETA unless this is a try push - jobdesc['optimizations'] = optimizations = [] - if config['project'] != 'try': - optimizations.append(['seta']) - run = jobdesc['run'] = {} run['using'] = 'mozharness-test' run['test'] = test From a3a9e7939fc3dd521923bb887fa221ebc9a684a5 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 28 Apr 2017 11:12:31 -0400 Subject: [PATCH 20/30] Bug 1345863: set the job_try_name for all tasks governed by -j; r=gps We have a `unittest_try_name` for all unit test tasks, and similarly a `talos_try_name`. The `-j` flag controls tasks known as "jobs" (although the word has dozens of other meanings, too). Some of those set `job_try_name`, but others do not and have special-case support in `try_option_syntax.py`. With this change, all "jobs" set `job_try_name` and the special-case is removed. MozReview-Commit-ID: 9hvW7wBIl2B --HG-- extra : rebase_source : 120b5e9e7aa8f81fe49e72f4dadafdbd145ac357 --- taskcluster/ci/android-stuff/kind.yml | 16 +--------------- taskcluster/ci/toolchain/kind.yml | 2 +- taskcluster/ci/toolchain/linux.yml | 14 +++++++------- taskcluster/ci/toolchain/macosx.yml | 6 +++--- taskcluster/ci/toolchain/windows.yml | 8 ++++---- taskcluster/taskgraph/transforms/try_job.py | 19 +++++++++++++++++++ taskcluster/taskgraph/try_option_syntax.py | 15 +-------------- 7 files changed, 36 insertions(+), 44 deletions(-) create mode 100644 taskcluster/taskgraph/transforms/try_job.py diff --git a/taskcluster/ci/android-stuff/kind.yml b/taskcluster/ci/android-stuff/kind.yml index 1afa01871a14..489ada6edd26 100644 --- a/taskcluster/ci/android-stuff/kind.yml +++ b/taskcluster/ci/android-stuff/kind.yml @@ -9,15 +9,13 @@ loader: taskgraph.loader.transform:loader transforms: + - taskgraph.transforms.try_job:transforms - taskgraph.transforms.android_stuff:transforms - taskgraph.transforms.task:transforms jobs: android-api-15-gradle-dependencies: description: "Android armv7 API 15+ gradle dependencies" - attributes: - build_platform: android-api-15-gradle-dependencies - build_type: opt index: product: mobile job-name: android-api-15-gradle-dependencies-opt @@ -66,9 +64,6 @@ jobs: android-test: description: "Android armv7 unit tests" - attributes: - build_platform: android-test - build_type: opt treeherder: platform: android-4-0-armv7-api15/opt kind: test @@ -115,9 +110,6 @@ jobs: android-lint: description: "Android lint" - attributes: - build_platform: android-lint - build_type: opt treeherder: platform: android-4-0-armv7-api15/opt kind: test @@ -186,9 +178,6 @@ jobs: android-checkstyle: description: "Android checkstyle" - attributes: - build_platform: android-checkstyle - build_type: opt treeherder: platform: android-4-0-armv7-api15/opt kind: test @@ -238,9 +227,6 @@ jobs: android-findbugs: description: "Android findbugs" - attributes: - build_platform: android-findbugs - build_type: opt treeherder: platform: android-4-0-armv7-api15/opt kind: test diff --git a/taskcluster/ci/toolchain/kind.yml b/taskcluster/ci/toolchain/kind.yml index 3a2d0327b53b..4adcada199e2 100644 --- a/taskcluster/ci/toolchain/kind.yml +++ b/taskcluster/ci/toolchain/kind.yml @@ -5,7 +5,7 @@ loader: taskgraph.loader.transform:loader transforms: - - taskgraph.transforms.build_attrs:transforms + - taskgraph.transforms.try_job:transforms - taskgraph.transforms.job:transforms - taskgraph.transforms.task:transforms diff --git a/taskcluster/ci/toolchain/linux.yml b/taskcluster/ci/toolchain/linux.yml index de59a053533c..5bc342f9623e 100644 --- a/taskcluster/ci/toolchain/linux.yml +++ b/taskcluster/ci/toolchain/linux.yml @@ -2,7 +2,7 @@ # 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/. -linux64-clang/opt: +linux64-clang: description: "Clang toolchain build" treeherder: kind: build @@ -22,7 +22,7 @@ linux64-clang/opt: - 'build/build-clang/**' - 'taskcluster/scripts/misc/tooltool-download.sh' -linux64-clang-tidy/opt: +linux64-clang-tidy: description: "Clang-tidy build" index: product: static-analysis @@ -46,7 +46,7 @@ linux64-clang-tidy/opt: - 'build/build-clang/**' - 'taskcluster/scripts/misc/tooltool-download.sh' -linux64-gcc/opt: +linux64-gcc: description: "GCC toolchain build" treeherder: kind: build @@ -64,7 +64,7 @@ linux64-gcc/opt: resources: - 'build/unix/build-gcc/**' -linux64-binutils/opt: +linux64-binutils: description: "Binutils toolchain build" treeherder: kind: build @@ -82,7 +82,7 @@ linux64-binutils/opt: resources: - 'build/unix/build-binutils/**' -linux64-cctools-port/opt: +linux64-cctools-port: description: "cctools-port toolchain build" treeherder: kind: build @@ -101,7 +101,7 @@ linux64-cctools-port/opt: resources: - 'taskcluster/scripts/misc/tooltool-download.sh' -linux64-hfsplus/opt: +linux64-hfsplus: description: "hfsplus toolchain build" treeherder: kind: build @@ -121,7 +121,7 @@ linux64-hfsplus/opt: - 'build/unix/build-hfsplus/**' - 'taskcluster/scripts/misc/tooltool-download.sh' -linux64-libdmg/opt: +linux64-libdmg: description: "libdmg-hfsplus toolchain build" treeherder: kind: build diff --git a/taskcluster/ci/toolchain/macosx.yml b/taskcluster/ci/toolchain/macosx.yml index 5e6174ac6494..a3879dcc9cc9 100644 --- a/taskcluster/ci/toolchain/macosx.yml +++ b/taskcluster/ci/toolchain/macosx.yml @@ -2,7 +2,7 @@ # 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/. -macosx64-clang/opt: +macosx64-clang: description: "Clang toolchain build" treeherder: kind: build @@ -22,7 +22,7 @@ macosx64-clang/opt: - 'build/build-clang/**' - 'taskcluster/scripts/misc/tooltool-download.sh' -macosx64-clang-tidy/opt: +macosx64-clang-tidy: description: "Clang-tidy build" index: product: static-analysis @@ -46,7 +46,7 @@ macosx64-clang-tidy/opt: - 'build/build-clang/**' - 'taskcluster/scripts/misc/tooltool-download.sh' -macosx64-cctools-port/opt: +macosx64-cctools-port: description: "cctools-port toolchain build" treeherder: kind: build diff --git a/taskcluster/ci/toolchain/windows.yml b/taskcluster/ci/toolchain/windows.yml index d1ad81783aa7..32a2f758b31c 100644 --- a/taskcluster/ci/toolchain/windows.yml +++ b/taskcluster/ci/toolchain/windows.yml @@ -2,7 +2,7 @@ # 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/. -win32-clang-cl/opt: +win32-clang-cl: description: "Clang-cl toolchain build" treeherder: kind: build @@ -20,7 +20,7 @@ win32-clang-cl/opt: - 'build/build-clang/**' - 'taskcluster/scripts/misc/build-clang-windows-helper32.sh' -win64-clang-cl/opt: +win64-clang-cl: description: "Clang-cl toolchain build" treeherder: kind: build @@ -38,7 +38,7 @@ win64-clang-cl/opt: - 'build/build-clang/**' - 'taskcluster/scripts/misc/build-clang-windows-helper64.sh' -win32-clang-tidy/opt: +win32-clang-tidy: description: "Clang-tidy toolchain build" index: product: static-analysis @@ -59,7 +59,7 @@ win32-clang-tidy/opt: - 'build/build-clang/**' - 'taskcluster/scripts/misc/build-clang-windows-helper32.sh' -win64-clang-tidy/opt: +win64-clang-tidy: description: "Clang-tidy toolchain build" index: product: static-analysis diff --git a/taskcluster/taskgraph/transforms/try_job.py b/taskcluster/taskgraph/transforms/try_job.py new file mode 100644 index 000000000000..e544acffd5e0 --- /dev/null +++ b/taskcluster/taskgraph/transforms/try_job.py @@ -0,0 +1,19 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +from __future__ import absolute_import, print_function, unicode_literals + +from taskgraph.transforms.base import TransformSequence + +transforms = TransformSequence() + + +@transforms.add +def set_job_try_name(config, jobs): + """ + For a task which is governed by `-j` in try syntax, set the `job_try_name` + attribute based on the job name. + """ + for job in jobs: + job.setdefault('attributes', {}).setdefault('job_try_name', job['name']) + yield job diff --git a/taskcluster/taskgraph/try_option_syntax.py b/taskcluster/taskgraph/try_option_syntax.py index 7685da9ee5a8..ab4357658ddf 100644 --- a/taskcluster/taskgraph/try_option_syntax.py +++ b/taskcluster/taskgraph/try_option_syntax.py @@ -32,12 +32,6 @@ BUILD_KINDS = set([ 'spidermonkey', ]) -# anything in this list is governed by -j, matching against the `build_platform` attribute -JOB_KINDS_MATCHING_BUILD_PLATFORM = set([ - 'toolchain', - 'android-stuff', -]) - # mapping from shortcut name (usable with -u) to a boolean function identifying # matching test names @@ -595,16 +589,9 @@ class TryOptionSyntax(object): if self.jobs is None or job_try_name in self.jobs: if self.platforms is None or attr('build_platform') not in self.platforms: return True - - if attr('kind') == 'test': + elif attr('kind') == 'test': return match_test(self.unittests, 'unittest_try_name') \ or match_test(self.talos, 'talos_try_name') - elif attr('kind') in JOB_KINDS_MATCHING_BUILD_PLATFORM: - # This will add 'job' tasks to the target set even if no try syntax was specified. - if not self.jobs: - return True - if attr('build_platform') in self.jobs: - return True elif attr('kind') in BUILD_KINDS: if attr('build_type') not in self.build_types: return False From b32a939c73ae99c55eb4cdc0503c6c65dd35c077 Mon Sep 17 00:00:00 2001 From: Imanol Fernandez Date: Mon, 15 May 2017 07:32:11 -0500 Subject: [PATCH 21/30] servo: Merge #16866 - Update mozjs_sys (from MortimerGoro:update_mozjs); r=nox See https://github.com/servo/mozjs/pull/118 Required for https://github.com/servo/servo/issues/11921 --- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). - [x] There are tests for these changes OR - [ ] These changes do not require tests because _____ Source-Repo: https://github.com/servo/servo Source-Revision: 9bc565362b8787768db77ac458c9fb8005e14023 --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 051657136a772c64ba7b67a8b418ac7bebcbec5e --- servo/Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servo/Cargo.lock b/servo/Cargo.lock index 24353999f284..7c61c2c02282 100644 --- a/servo/Cargo.lock +++ b/servo/Cargo.lock @@ -1675,7 +1675,7 @@ dependencies = [ [[package]] name = "mozjs_sys" version = "0.0.0" -source = "git+https://github.com/servo/mozjs#29132f6790dd0c066f10fc398a7071f6a5b6bc65" +source = "git+https://github.com/servo/mozjs#834ce35c3f008010213351107b68f397989d2ffd" dependencies = [ "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", From ae158ad730508c28c8b83e36858e0ff4b0c76b3b Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Mon, 15 May 2017 13:22:25 +0900 Subject: [PATCH 22/30] Bug 1364814 - Use RAII class to set and restore editor flags. r=masayuki input.value setter removes editor flags and max-length to set value. To clean up code, I would like to use RAII class to set and restore it. Actually, when the frame is being destroyed by InsertText, it isn't restored. But it might be safe since the flags is set again on nsTextEditorState::PrepareEditor by focus. MozReview-Commit-ID: J0OYYluWD8z --HG-- extra : rebase_source : b0489a381cdea76fe98f328cbd88cd18d2576a93 --- dom/html/nsTextEditorState.cpp | 84 +++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 27 deletions(-) diff --git a/dom/html/nsTextEditorState.cpp b/dom/html/nsTextEditorState.cpp index 4aa2a25e5277..256885696c3e 100644 --- a/dom/html/nsTextEditorState.cpp +++ b/dom/html/nsTextEditorState.cpp @@ -133,6 +133,45 @@ private: nsTextEditorState* mTextEditorState; }; +class MOZ_RAII AutoRestoreEditorState final +{ +public: + explicit AutoRestoreEditorState(nsIEditor* aEditor, + nsIPlaintextEditor* aPlaintextEditor + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mEditor(aEditor) + , mPlaintextEditor(aPlaintextEditor) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + MOZ_ASSERT(mEditor); + MOZ_ASSERT(mPlaintextEditor); + + uint32_t flags; + mEditor->GetFlags(&mSavedFlags); + flags = mSavedFlags; + flags &= ~(nsIPlaintextEditor::eEditorDisabledMask); + flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask); + flags |= nsIPlaintextEditor::eEditorDontEchoPassword; + mEditor->SetFlags(flags); + + mPlaintextEditor->GetMaxTextLength(&mSavedMaxLength); + mPlaintextEditor->SetMaxTextLength(-1); + } + + ~AutoRestoreEditorState() + { + mPlaintextEditor->SetMaxTextLength(mSavedMaxLength); + mEditor->SetFlags(mSavedFlags); + } + +private: + nsIEditor* mEditor; + nsIPlaintextEditor* mPlaintextEditor; + uint32_t mSavedFlags; + int32_t mSavedMaxLength; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + /*static*/ bool nsITextControlElement::GetWrapPropertyEnum(nsIContent* aContent, @@ -2551,8 +2590,9 @@ nsTextEditorState::SetValue(const nsAString& aValue, uint32_t aFlags) if (domSel) { selPriv = do_QueryInterface(domSel); - if (selPriv) + if (selPriv) { selPriv->StartBatchChanges(); + } } nsCOMPtr kungFuDeathGrip = mSelCon.get(); @@ -2577,34 +2617,25 @@ nsTextEditorState::SetValue(const nsAString& aValue, uint32_t aFlags) valueSetter.Init(); - // get the flags, remove readonly and disabled, set the value, - // restore flags - uint32_t flags, savedFlags; - mEditor->GetFlags(&savedFlags); - flags = savedFlags; - flags &= ~(nsIPlaintextEditor::eEditorDisabledMask); - flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask); - flags |= nsIPlaintextEditor::eEditorDontEchoPassword; - mEditor->SetFlags(flags); + // get the flags, remove readonly, disabled and max-length, + // set the value, restore flags + { + AutoRestoreEditorState restoreState(mEditor, plaintextEditor); - mTextListener->SettingValue(true); - bool notifyValueChanged = !!(aFlags & eSetValue_Notify); - mTextListener->SetValueChanged(notifyValueChanged); + mTextListener->SettingValue(true); + bool notifyValueChanged = !!(aFlags & eSetValue_Notify); + mTextListener->SetValueChanged(notifyValueChanged); - // Also don't enforce max-length here - int32_t savedMaxLength; - plaintextEditor->GetMaxTextLength(&savedMaxLength); - plaintextEditor->SetMaxTextLength(-1); + if (insertValue.IsEmpty()) { + mEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip); + } else { + plaintextEditor->InsertText(insertValue); + } - if (insertValue.IsEmpty()) { - mEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip); - } else { - plaintextEditor->InsertText(insertValue); + mTextListener->SetValueChanged(true); + mTextListener->SettingValue(false); } - mTextListener->SetValueChanged(true); - mTextListener->SettingValue(false); - if (!weakFrame.IsAlive()) { // If the frame was destroyed because of a flush somewhere inside // InsertText, mBoundFrame here will be false. But it's also possible @@ -2623,10 +2654,9 @@ nsTextEditorState::SetValue(const nsAString& aValue, uint32_t aFlags) } } - plaintextEditor->SetMaxTextLength(savedMaxLength); - mEditor->SetFlags(savedFlags); - if (selPriv) + if (selPriv) { selPriv->EndBatchChanges(); + } } } } else { From 440092e29553ea67ae9bc228dfcd2c54155ff557 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Mon, 15 May 2017 09:13:38 -0500 Subject: [PATCH 23/30] =?UTF-8?q?servo:=20Merge=20#16859=20-=20Rewrite=20s?= =?UTF-8?q?tyle=20images=20with=20a=20good=20dose=20of=20generics=20?= =?UTF-8?q?=F0=9F=92=89=20(from=20nox:gradients);=20r=3Demilio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Source-Repo: https://github.com/servo/servo Source-Revision: eb7314b4122ba981cdc98204660f9fa1ef43e698 --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 761a774e8e8a27e96e5047377166993fee80d201 --- .../components/layout/display_list_builder.rs | 118 +- servo/components/script/dom/element.rs | 6 +- servo/components/style/gecko/conversions.rs | 88 +- .../components/style/properties/gecko.mako.rs | 4 +- .../properties/longhand/background.mako.rs | 6 +- .../style/properties/longhand/border.mako.rs | 8 +- .../style/properties/longhand/svg.mako.rs | 7 +- .../components/style/values/computed/image.rs | 749 ++-------- .../style/values/computed/length.rs | 3 +- servo/components/style/values/computed/mod.rs | 3 +- .../components/style/values/generics/image.rs | 636 +++++++++ servo/components/style/values/generics/mod.rs | 1 + .../style/values/specified/image.rs | 1210 ++++++++--------- .../style/values/specified/length.rs | 16 +- .../components/style/values/specified/mod.rs | 29 +- servo/ports/geckolib/glue.rs | 8 +- servo/tests/unit/style/parsing/image.rs | 76 +- .../unit/style/properties/serialization.rs | 17 +- 18 files changed, 1509 insertions(+), 1476 deletions(-) create mode 100644 servo/components/style/values/generics/image.rs diff --git a/servo/components/layout/display_list_builder.rs b/servo/components/layout/display_list_builder.rs index 5f9a783c8968..c3ac7f09048c 100644 --- a/servo/components/layout/display_list_builder.rs +++ b/servo/components/layout/display_list_builder.rs @@ -57,12 +57,14 @@ use style::properties::{self, ServoComputedValues}; use style::properties::longhands::border_image_repeat::computed_value::RepeatKeyword; use style::properties::style_structs; use style::servo::restyle_damage::REPAINT; -use style::values::{Either, RGBA, computed}; -use style::values::computed::{AngleOrCorner, Gradient, GradientItem, GradientKind, LengthOrPercentage}; -use style::values::computed::{LengthOrPercentageOrAuto, LengthOrKeyword, LengthOrPercentageOrKeyword}; -use style::values::computed::{NumberOrPercentage, Position}; -use style::values::computed::image::{EndingShape, SizeKeyword}; -use style::values::specified::{HorizontalDirection, VerticalDirection}; +use style::values::{Either, RGBA}; +use style::values::computed::{Gradient, GradientItem, LengthOrPercentage}; +use style::values::computed::{LengthOrPercentageOrAuto, NumberOrPercentage, Position}; +use style::values::computed::image::{EndingShape, LineDirection}; +use style::values::generics::image::{Circle, Ellipse, EndingShape as GenericEndingShape}; +use style::values::generics::image::{GradientItem as GenericGradientItem, GradientKind}; +use style::values::generics::image::{Image, ShapeExtent}; +use style::values::specified::position::{X, Y}; use style_traits::CSSPixel; use style_traits::cursor::Cursor; use table_cell::CollapsedBordersForCell; @@ -390,7 +392,7 @@ pub trait FragmentDisplayListBuilding { fn convert_linear_gradient(&self, bounds: &Rect, stops: &[GradientItem], - angle_or_corner: &AngleOrCorner, + direction: &LineDirection, repeating: bool, style: &ServoComputedValues) -> display_list::Gradient; @@ -610,7 +612,7 @@ fn convert_gradient_stops(gradient_items: &[GradientItem], // Only keep the color stops, discard the color interpolation hints. let mut stop_items = gradient_items.iter().filter_map(|item| { match *item { - GradientItem::ColorStop(ref stop) => Some(*stop), + GenericGradientItem::ColorStop(ref stop) => Some(*stop), _ => None, } }).collect::>(); @@ -758,36 +760,46 @@ fn get_ellipse_radius(size: &Size2D, center: &Point2D, cmp: F) -> Siz /// Determines the radius of a circle if it was not explictly provided. /// https://drafts.csswg.org/css-images-3/#typedef-size -fn convert_circle_size_keyword(keyword: SizeKeyword, +fn convert_circle_size_keyword(keyword: ShapeExtent, size: &Size2D, center: &Point2D) -> Size2D { - use style::values::computed::image::SizeKeyword::*; let radius = match keyword { - ClosestSide | Contain => { + ShapeExtent::ClosestSide | ShapeExtent::Contain => { let dist = get_distance_to_sides(size, center, ::std::cmp::min); ::std::cmp::min(dist.width, dist.height) } - FarthestSide => { + ShapeExtent::FarthestSide => { let dist = get_distance_to_sides(size, center, ::std::cmp::max); ::std::cmp::max(dist.width, dist.height) } - ClosestCorner => get_distance_to_corner(size, center, ::std::cmp::min), - FarthestCorner | Cover => get_distance_to_corner(size, center, ::std::cmp::max), + ShapeExtent::ClosestCorner => { + get_distance_to_corner(size, center, ::std::cmp::min) + }, + ShapeExtent::FarthestCorner | ShapeExtent::Cover => { + get_distance_to_corner(size, center, ::std::cmp::max) + }, }; Size2D::new(radius, radius) } /// Determines the radius of an ellipse if it was not explictly provided. /// https://drafts.csswg.org/css-images-3/#typedef-size -fn convert_ellipse_size_keyword(keyword: SizeKeyword, +fn convert_ellipse_size_keyword(keyword: ShapeExtent, size: &Size2D, center: &Point2D) -> Size2D { - use style::values::computed::image::SizeKeyword::*; match keyword { - ClosestSide | Contain => get_distance_to_sides(size, center, ::std::cmp::min), - FarthestSide => get_distance_to_sides(size, center, ::std::cmp::max), - ClosestCorner => get_ellipse_radius(size, center, ::std::cmp::min), - FarthestCorner | Cover => get_ellipse_radius(size, center, ::std::cmp::max), + ShapeExtent::ClosestSide | ShapeExtent::Contain => { + get_distance_to_sides(size, center, ::std::cmp::min) + }, + ShapeExtent::FarthestSide => { + get_distance_to_sides(size, center, ::std::cmp::max) + }, + ShapeExtent::ClosestCorner => { + get_ellipse_radius(size, center, ::std::cmp::min) + }, + ShapeExtent::FarthestCorner | ShapeExtent::Cover => { + get_ellipse_radius(size, center, ::std::cmp::max) + }, } } @@ -853,9 +865,9 @@ impl FragmentDisplayListBuilding for Fragment { // http://www.w3.org/TR/CSS21/colors.html#background let background = style.get_background(); for (i, background_image) in background.background_image.0.iter().enumerate().rev() { - match background_image.0 { - None => {} - Some(computed::Image::Gradient(ref gradient)) => { + match *background_image { + Either::First(_) => {} + Either::Second(Image::Gradient(ref gradient)) => { self.build_display_list_for_background_gradient(state, display_list_section, &absolute_bounds, @@ -864,7 +876,7 @@ impl FragmentDisplayListBuilding for Fragment { gradient, style); } - Some(computed::Image::Url(ref image_url)) => { + Either::Second(Image::Url(ref image_url)) => { if let Some(url) = image_url.url() { self.build_display_list_for_background_image(state, style, @@ -875,10 +887,10 @@ impl FragmentDisplayListBuilding for Fragment { i); } } - Some(computed::Image::ImageRect(_)) => { + Either::Second(Image::Rect(_)) => { // TODO: Implement `-moz-image-rect` } - Some(computed::Image::Element(_)) => { + Either::Second(Image::Element(_)) => { // TODO: Implement `-moz-element` } } @@ -1097,26 +1109,26 @@ impl FragmentDisplayListBuilding for Fragment { fn convert_linear_gradient(&self, bounds: &Rect, stops: &[GradientItem], - angle_or_corner: &AngleOrCorner, + direction: &LineDirection, repeating: bool, style: &ServoComputedValues) -> display_list::Gradient { - let angle = match *angle_or_corner { - AngleOrCorner::Angle(angle) => angle.radians(), - AngleOrCorner::Corner(horizontal, vertical) => { + let angle = match *direction { + LineDirection::Angle(angle) => angle.radians(), + LineDirection::Corner(horizontal, vertical) => { // This the angle for one of the diagonals of the box. Our angle // will either be this one, this one + PI, or one of the other // two perpendicular angles. let atan = (bounds.size.height.to_f32_px() / bounds.size.width.to_f32_px()).atan(); match (horizontal, vertical) { - (HorizontalDirection::Right, VerticalDirection::Bottom) + (X::Right, Y::Bottom) => f32::consts::PI - atan, - (HorizontalDirection::Left, VerticalDirection::Bottom) + (X::Left, Y::Bottom) => f32::consts::PI + atan, - (HorizontalDirection::Right, VerticalDirection::Top) + (X::Right, Y::Top) => atan, - (HorizontalDirection::Left, VerticalDirection::Top) + (X::Left, Y::Top) => -atan, } } @@ -1170,16 +1182,18 @@ impl FragmentDisplayListBuilding for Fragment { let center = Point2D::new(specified(center.horizontal, bounds.size.width), specified(center.vertical, bounds.size.height)); let radius = match *shape { - EndingShape::Circle(LengthOrKeyword::Length(length)) - => Size2D::new(length, length), - EndingShape::Circle(LengthOrKeyword::Keyword(word)) - => convert_circle_size_keyword(word, &bounds.size, ¢er), - EndingShape::Ellipse(LengthOrPercentageOrKeyword::LengthOrPercentage(horizontal, - vertical)) - => Size2D::new(specified(horizontal, bounds.size.width), - specified(vertical, bounds.size.height)), - EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(word)) - => convert_ellipse_size_keyword(word, &bounds.size, ¢er), + GenericEndingShape::Circle(Circle::Radius(length)) => { + Size2D::new(length, length) + }, + GenericEndingShape::Circle(Circle::Extent(extent)) => { + convert_circle_size_keyword(extent, &bounds.size, ¢er) + }, + GenericEndingShape::Ellipse(Ellipse::Radii(x, y)) => { + Size2D::new(specified(x, bounds.size.width), specified(y, bounds.size.height)) + }, + GenericEndingShape::Ellipse(Ellipse::Extent(extent)) => { + convert_ellipse_size_keyword(extent, &bounds.size, ¢er) + }, }; let mut stops = convert_gradient_stops(stops, radius.width, style); @@ -1221,7 +1235,7 @@ impl FragmentDisplayListBuilding for Fragment { style.get_cursor(Cursor::Default), display_list_section); - let display_item = match gradient.gradient_kind { + let display_item = match gradient.kind { GradientKind::Linear(ref angle_or_corner) => { let gradient = self.convert_linear_gradient(&bounds, &gradient.items[..], @@ -1342,8 +1356,8 @@ impl FragmentDisplayListBuilding for Fragment { style.get_cursor(Cursor::Default), display_list_section); - match border_style_struct.border_image_source.0 { - None => { + match border_style_struct.border_image_source { + Either::First(_) => { state.add_display_item(DisplayItem::Border(box BorderDisplayItem { base: base, border_widths: border.to_physical(style.writing_mode), @@ -1357,8 +1371,8 @@ impl FragmentDisplayListBuilding for Fragment { }), })); } - Some(computed::Image::Gradient(ref gradient)) => { - match gradient.gradient_kind { + Either::Second(Image::Gradient(ref gradient)) => { + match gradient.kind { GradientKind::Linear(angle_or_corner) => { let grad = self.convert_linear_gradient(&bounds, &gradient.items[..], @@ -1398,13 +1412,13 @@ impl FragmentDisplayListBuilding for Fragment { } } } - Some(computed::Image::ImageRect(..)) => { + Either::Second(Image::Rect(..)) => { // TODO: Handle border-image with `-moz-image-rect`. } - Some(computed::Image::Element(..)) => { + Either::Second(Image::Element(..)) => { // TODO: Handle border-image with `-moz-element`. } - Some(computed::Image::Url(ref image_url)) => { + Either::Second(Image::Url(ref image_url)) => { if let Some(url) = image_url.url() { let webrender_image = state.layout_context .get_webrender_image_for_url(self.node, diff --git a/servo/components/script/dom/element.rs b/servo/components/script/dom/element.rs index d54b79cdc28e..aa32370127c0 100644 --- a/servo/components/script/dom/element.rs +++ b/servo/components/script/dom/element.rs @@ -109,7 +109,7 @@ use style::sink::Push; use style::stylearc::Arc; use style::stylist::ApplicableDeclarationBlock; use style::thread_state; -use style::values::CSSFloat; +use style::values::{CSSFloat, Either}; use style::values::specified::{self, CSSColor}; use stylesheet_loader::StylesheetOwner; @@ -427,9 +427,7 @@ impl LayoutElementHelpers for LayoutJS { shared_lock, PropertyDeclaration::BackgroundImage( background_image::SpecifiedValue(vec![ - background_image::single_value::SpecifiedValue(Some( - specified::Image::for_cascade(url.into()) - )) + Either::Second(specified::Image::for_cascade(url.into())) ])))); } diff --git a/servo/components/style/gecko/conversions.rs b/servo/components/style/gecko/conversions.rs index bb299a74db13..720c1bd2315d 100644 --- a/servo/components/style/gecko/conversions.rs +++ b/servo/components/style/gecko/conversions.rs @@ -16,8 +16,9 @@ use gecko_bindings::structs::{nsCSSUnit, nsStyleCoord_CalcValue, nsStyleImage}; use gecko_bindings::structs::{nsresult, SheetType}; use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordDataMut}; use stylesheets::{Origin, RulesMutateError}; -use values::computed::{Angle, CalcLengthOrPercentage, Gradient, GradientItem, Image}; +use values::computed::{Angle, CalcLengthOrPercentage, Gradient, Image}; use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; +use values::generics::image::{CompatMode, Image as GenericImage, GradientItem}; impl From for nsStyleCoord_CalcValue { fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue { @@ -138,10 +139,10 @@ impl nsStyleImage { /// Set a given Servo `Image` value into this `nsStyleImage`. pub fn set(&mut self, image: Image, cacheable: &mut bool) { match image { - Image::Gradient(gradient) => { + GenericImage::Gradient(gradient) => { self.set_gradient(gradient) }, - Image::Url(ref url) => { + GenericImage::Url(ref url) => { unsafe { Gecko_SetUrlImageValue(self, url.for_ffi()); // We unfortunately must make any url() value uncacheable, since @@ -154,7 +155,7 @@ impl nsStyleImage { *cacheable = false; } }, - Image::ImageRect(ref image_rect) => { + GenericImage::Rect(ref image_rect) => { unsafe { Gecko_SetUrlImageValue(self, image_rect.url.for_ffi()); Gecko_InitializeImageCropRect(self); @@ -176,7 +177,7 @@ impl nsStyleImage { image_rect.left.to_gecko_style_coord(&mut rect.data_at_mut(3)); } } - Image::Element(ref element) => { + GenericImage::Element(ref element) => { unsafe { Gecko_SetImageElement(self, element.as_ptr()); } @@ -191,9 +192,9 @@ impl nsStyleImage { use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE}; use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE}; use gecko_bindings::structs::nsStyleCoord; - use values::computed::{AngleOrCorner, GradientKind, GradientShape, LengthOrKeyword}; - use values::computed::LengthOrPercentageOrKeyword; - use values::specified::{HorizontalDirection, SizeKeyword, VerticalDirection}; + use values::computed::image::LineDirection; + use values::generics::image::{Circle, Ellipse, EndingShape, GradientKind, ShapeExtent}; + use values::specified::position::{X, Y}; let stop_count = gradient.items.len(); if stop_count >= ::std::u32::MAX as usize { @@ -201,32 +202,32 @@ impl nsStyleImage { return; } - let gecko_gradient = match gradient.gradient_kind { - GradientKind::Linear(angle_or_corner) => { + let gecko_gradient = match gradient.kind { + GradientKind::Linear(direction) => { let gecko_gradient = unsafe { Gecko_CreateGradient(NS_STYLE_GRADIENT_SHAPE_LINEAR as u8, NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as u8, gradient.repeating, - /* legacy_syntax = */ false, + gradient.compat_mode == CompatMode::WebKit, stop_count as u32) }; - match angle_or_corner { - AngleOrCorner::Angle(angle) => { + match direction { + LineDirection::Angle(angle) => { unsafe { (*gecko_gradient).mAngle.set(angle); (*gecko_gradient).mBgPosX.set_value(CoordDataValue::None); (*gecko_gradient).mBgPosY.set_value(CoordDataValue::None); } }, - AngleOrCorner::Corner(horiz, vert) => { + LineDirection::Corner(horiz, vert) => { let percent_x = match horiz { - HorizontalDirection::Left => 0.0, - HorizontalDirection::Right => 1.0, + X::Left => 0.0, + X::Right => 1.0, }; let percent_y = match vert { - VerticalDirection::Top => 0.0, - VerticalDirection::Bottom => 1.0, + Y::Top => 0.0, + Y::Bottom => 1.0, }; unsafe { @@ -243,28 +244,28 @@ impl nsStyleImage { GradientKind::Radial(shape, position) => { let keyword_to_gecko_size = |keyword| { match keyword { - SizeKeyword::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, - SizeKeyword::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE, - SizeKeyword::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER, - SizeKeyword::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, - SizeKeyword::Contain => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, - SizeKeyword::Cover => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, + ShapeExtent::ClosestSide => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, + ShapeExtent::FarthestSide => NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE, + ShapeExtent::ClosestCorner => NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER, + ShapeExtent::FarthestCorner => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, + ShapeExtent::Contain => NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, + ShapeExtent::Cover => NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, } }; let (gecko_shape, gecko_size) = match shape { - GradientShape::Circle(ref length) => { - let size = match *length { - LengthOrKeyword::Keyword(keyword) => { - keyword_to_gecko_size(keyword) + EndingShape::Circle(ref circle) => { + let size = match *circle { + Circle::Extent(extent) => { + keyword_to_gecko_size(extent) }, _ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE, }; (NS_STYLE_GRADIENT_SHAPE_CIRCULAR as u8, size as u8) }, - GradientShape::Ellipse(ref length) => { - let size = match *length { - LengthOrPercentageOrKeyword::Keyword(keyword) => { - keyword_to_gecko_size(keyword) + EndingShape::Ellipse(ref ellipse) => { + let size = match *ellipse { + Ellipse::Extent(extent) => { + keyword_to_gecko_size(extent) }, _ => NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE, }; @@ -276,7 +277,7 @@ impl nsStyleImage { Gecko_CreateGradient(gecko_shape, gecko_size, gradient.repeating, - /* legacy_syntax = */ false, + gradient.compat_mode == CompatMode::WebKit, stop_count as u32) }; @@ -289,22 +290,19 @@ impl nsStyleImage { // Setting radius values depending shape match shape { - GradientShape::Circle(length) => { - if let LengthOrKeyword::Length(len) = length { - unsafe { - (*gecko_gradient).mRadiusX.set_value(CoordDataValue::Coord(len.0)); - (*gecko_gradient).mRadiusY.set_value(CoordDataValue::Coord(len.0)); - } + EndingShape::Circle(Circle::Radius(length)) => { + unsafe { + (*gecko_gradient).mRadiusX.set_value(CoordDataValue::Coord(length.0)); + (*gecko_gradient).mRadiusY.set_value(CoordDataValue::Coord(length.0)); } }, - GradientShape::Ellipse(length) => { - if let LengthOrPercentageOrKeyword::LengthOrPercentage(first_len, second_len) = length { - unsafe { - (*gecko_gradient).mRadiusX.set(first_len); - (*gecko_gradient).mRadiusY.set(second_len); - } + EndingShape::Ellipse(Ellipse::Radii(x, y)) => { + unsafe { + (*gecko_gradient).mRadiusX.set(x); + (*gecko_gradient).mRadiusY.set(y); } }, + _ => {}, } unsafe { (*gecko_gradient).mBgPosX.set(position.horizontal); diff --git a/servo/components/style/properties/gecko.mako.rs b/servo/components/style/properties/gecko.mako.rs index 60acde9e18b7..b86f18534cf1 100644 --- a/servo/components/style/properties/gecko.mako.rs +++ b/servo/components/style/properties/gecko.mako.rs @@ -927,7 +927,7 @@ fn static_assert() { Gecko_SetNullImageValue(&mut self.gecko.mBorderImageSource); } - if let Some(image) = image.0 { + if let Either::Second(image) = image { self.gecko.mBorderImageSource.set(image, &mut false) } } @@ -2879,7 +2879,7 @@ fn static_assert() { for (image, geckoimage) in images.zip(self.gecko.${image_layers_field} .mLayers.iter_mut()) { - if let Some(image) = image.0 { + if let Either::Second(image) = image { geckoimage.mImage.set(image, cacheable) } } diff --git a/servo/components/style/properties/longhand/background.mako.rs b/servo/components/style/properties/longhand/background.mako.rs index 3b50cbae01a0..e5c3d628f64c 100644 --- a/servo/components/style/properties/longhand/background.mako.rs +++ b/servo/components/style/properties/longhand/background.mako.rs @@ -12,9 +12,9 @@ ${helpers.predefined_type("background-color", "CSSColor", spec="https://drafts.csswg.org/css-backgrounds/#background-color", animation_value_type="IntermediateColor", complex_color=True)} -${helpers.predefined_type("background-image", "LayerImage", - initial_value="computed_value::T(None)", - initial_specified_value="SpecifiedValue(None)", +${helpers.predefined_type("background-image", "ImageLayer", + initial_value="Either::First(None_)", + initial_specified_value="Either::First(None_)", spec="https://drafts.csswg.org/css-backgrounds/#the-background-image", vector="True", animation_value_type="none", diff --git a/servo/components/style/properties/longhand/border.mako.rs b/servo/components/style/properties/longhand/border.mako.rs index 6f07db690382..f18ae1544289 100644 --- a/servo/components/style/properties/longhand/border.mako.rs +++ b/servo/components/style/properties/longhand/border.mako.rs @@ -190,9 +190,9 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-float-edge)", animation_value_type="none")} -${helpers.predefined_type("border-image-source", "LayerImage", - initial_value="computed_value::T(None)", - initial_specified_value="SpecifiedValue(None)", +${helpers.predefined_type("border-image-source", "ImageLayer", + initial_value="Either::First(None_)", + initial_specified_value="Either::First(None_)", spec="https://drafts.csswg.org/css-backgrounds/#the-background-image", vector=False, animation_value_type="none", @@ -717,7 +717,7 @@ ${helpers.predefined_type("border-image-source", "LayerImage", let mut values = vec![]; for _ in 0..4 { - let value = input.try(|input| NumberOrPercentage::parse(context, input)); + let value = input.try(|input| NumberOrPercentage::parse_non_negative(context, input)); match value { Ok(val) => values.push(val), Err(_) => break, diff --git a/servo/components/style/properties/longhand/svg.mako.rs b/servo/components/style/properties/longhand/svg.mako.rs index c1327a6ca2ed..540c522c09d5 100644 --- a/servo/components/style/properties/longhand/svg.mako.rs +++ b/servo/components/style/properties/longhand/svg.mako.rs @@ -141,9 +141,10 @@ ${helpers.single_keyword("mask-composite", extra_prefixes="webkit", animation_value_type="none", spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite")} -${helpers.predefined_type("mask-image", "LayerImage", - initial_value="computed_value::T(None)", - initial_specified_value="SpecifiedValue(None)", + +${helpers.predefined_type("mask-image", "ImageLayer", + initial_value="Either::First(None_)", + initial_specified_value="Either::First(None_)", spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image", vector=True, products="gecko", diff --git a/servo/components/style/values/computed/image.rs b/servo/components/style/values/computed/image.rs index 01a6106f3d32..5f51dd22f856 100644 --- a/servo/components/style/values/computed/image.rs +++ b/servo/components/style/values/computed/image.rs @@ -7,687 +7,126 @@ //! //! [image]: https://drafts.csswg.org/css-images/#image-values -use Atom; -use cssparser::{Color as CSSColor, serialize_identifier}; +use cssparser::Color as CSSColor; use std::f32::consts::PI; use std::fmt; use style_traits::ToCss; +use values::{Either, None_}; use values::computed::{Angle, Context, Length, LengthOrPercentage, NumberOrPercentage, ToComputedValue}; use values::computed::position::Position; -use values::specified::{self, HorizontalDirection, VerticalDirection}; -use values::specified::image::CompatMode; -use values::specified::url::SpecifiedUrl; +use values::generics::image::{CompatMode, ColorStop as GenericColorStop, EndingShape as GenericEndingShape}; +use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem}; +use values::generics::image::{Image as GenericImage, GradientKind as GenericGradientKind}; +use values::generics::image::{ImageRect as GenericImageRect, LineDirection as GenericLineDirection}; +use values::specified::image::LineDirection as SpecifiedLineDirection; +use values::specified::position::{X, Y}; -pub use values::specified::SizeKeyword; - -impl ToComputedValue for specified::Image { - type ComputedValue = Image; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Image { - match *self { - specified::Image::Url(ref url_value) => { - Image::Url(url_value.clone()) - }, - specified::Image::Gradient(ref gradient) => { - Image::Gradient(gradient.to_computed_value(context)) - }, - specified::Image::ImageRect(ref image_rect) => { - Image::ImageRect(image_rect.to_computed_value(context)) - }, - specified::Image::Element(ref selector) => { - Image::Element(selector.clone()) - } - } - } - - #[inline] - fn from_computed_value(computed: &Image) -> Self { - match *computed { - Image::Url(ref url_value) => { - specified::Image::Url(url_value.clone()) - }, - Image::Gradient(ref linear_gradient) => { - specified::Image::Gradient( - ToComputedValue::from_computed_value(linear_gradient) - ) - }, - Image::ImageRect(ref image_rect) => { - specified::Image::ImageRect( - ToComputedValue::from_computed_value(image_rect) - ) - }, - Image::Element(ref selector) => { - specified::Image::Element(selector.clone()) - }, - } - } -} +/// A computed image layer. +pub type ImageLayer = Either; /// Computed values for an image according to CSS-IMAGES. /// https://drafts.csswg.org/css-images/#image-values -#[derive(Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub enum Image { - Url(SpecifiedUrl), - Gradient(Gradient), - ImageRect(ImageRect), - Element(Atom), -} - -impl fmt::Debug for Image { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Image::Url(ref url) => url.to_css(f), - Image::Gradient(ref grad) => { - if grad.repeating { - let _ = write!(f, "repeating-"); - } - match grad.gradient_kind { - GradientKind::Linear(_) => write!(f, "linear-gradient({:?})", grad), - GradientKind::Radial(_, _) => write!(f, "radial-gradient({:?})", grad), - } - }, - Image::ImageRect(ref image_rect) => write!(f, "{:?}", image_rect), - Image::Element(ref selector) => { - f.write_str("-moz-element(#")?; - serialize_identifier(&*selector.to_string(), f)?; - f.write_str(")") - }, - } - } -} - -impl ToCss for Image { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - Image::Url(ref url) => url.to_css(dest), - Image::Gradient(ref gradient) => gradient.to_css(dest), - Image::ImageRect(ref image_rect) => image_rect.to_css(dest), - Image::Element(ref selector) => { - dest.write_str("-moz-element(#")?; - // FIXME: We should get rid of these intermediate strings. - serialize_identifier(&*selector.to_string(), dest)?; - dest.write_str(")") - }, - } - } -} +pub type Image = GenericImage; /// Computed values for a CSS gradient. /// https://drafts.csswg.org/css-images/#gradients -#[derive(Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct Gradient { - /// The color stops. - pub items: Vec, - /// True if this is a repeating gradient. - pub repeating: bool, - /// Gradient kind can be linear or radial. - pub gradient_kind: GradientKind, - /// Compatibility mode. - pub compat_mode: CompatMode, -} +pub type Gradient = GenericGradient< + LineDirection, + Length, + LengthOrPercentage, + Position, + CSSColor, +>; -impl ToCss for Gradient { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - if self.compat_mode == CompatMode::WebKit { - try!(dest.write_str("-webkit-")); - } - if self.repeating { - try!(dest.write_str("repeating-")); - } - match self.gradient_kind { - GradientKind::Linear(angle_or_corner) => { - try!(dest.write_str("linear-gradient(")); - try!(angle_or_corner.to_css(dest, self.compat_mode)); - }, - GradientKind::Radial(ref shape, position) => { - try!(dest.write_str("radial-gradient(")); - try!(shape.to_css(dest)); - try!(dest.write_str(" at ")); - try!(position.to_css(dest)); - }, - } - for item in &self.items { - try!(dest.write_str(", ")); - try!(item.to_css(dest)); - } - try!(dest.write_str(")")); - Ok(()) - } -} - -impl fmt::Debug for Gradient { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.gradient_kind { - GradientKind::Linear(angle_or_corner) => { - let _ = write!(f, "{:?}", angle_or_corner); - }, - GradientKind::Radial(ref shape, position) => { - let _ = write!(f, "{:?} at {:?}", shape, position); - }, - } - - for item in &self.items { - let _ = write!(f, ", {:?}", item); - } - Ok(()) - } -} - -impl ToComputedValue for specified::Gradient { - type ComputedValue = Gradient; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Gradient { - let specified::Gradient { - ref items, - repeating, - ref gradient_kind, - compat_mode, - } = *self; - Gradient { - items: items.iter().map(|s| s.to_computed_value(context)).collect(), - repeating: repeating, - gradient_kind: gradient_kind.to_computed_value(context), - compat_mode: compat_mode, - } - } - - #[inline] - fn from_computed_value(computed: &Gradient) -> Self { - let Gradient { - ref items, - repeating, - ref gradient_kind, - compat_mode, - } = *computed; - specified::Gradient { - items: items.iter().map(ToComputedValue::from_computed_value).collect(), - repeating: repeating, - gradient_kind: ToComputedValue::from_computed_value(gradient_kind), - compat_mode: compat_mode, - } - } -} - -/// Computed values for CSS linear or radial gradients. -/// https://drafts.csswg.org/css-images/#gradients -#[derive(Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub enum GradientKind { - Linear(AngleOrCorner), - Radial(EndingShape, Position), -} - -impl ToComputedValue for specified::GradientKind { - type ComputedValue = GradientKind; - - #[inline] - fn to_computed_value(&self, context: &Context) -> GradientKind { - match *self { - specified::GradientKind::Linear(angle_or_corner) => { - GradientKind::Linear(angle_or_corner.to_computed_value(context)) - }, - specified::GradientKind::Radial(ref shape, ref position) => { - GradientKind::Radial(shape.to_computed_value(context), - position.to_computed_value(context)) - }, - } - } - #[inline] - fn from_computed_value(computed: &GradientKind) -> Self { - match *computed { - GradientKind::Linear(angle_or_corner) => { - specified::GradientKind::Linear(ToComputedValue::from_computed_value(&angle_or_corner)) - }, - GradientKind::Radial(ref shape, position) => { - specified::GradientKind::Radial(ToComputedValue::from_computed_value(shape), - ToComputedValue::from_computed_value(&position)) - }, - } - } -} - -/// Specified values for color stops and interpolation hints. -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum GradientItem { - /// A color stop. - ColorStop(ColorStop), - /// An interpolation hint. - InterpolationHint(LengthOrPercentage), -} - -impl ToCss for GradientItem { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - GradientItem::ColorStop(stop) => stop.to_css(dest), - GradientItem::InterpolationHint(hint) => hint.to_css(dest), - } - } -} - -impl ToComputedValue for specified::GradientItem { - type ComputedValue = GradientItem; - - #[inline] - fn to_computed_value(&self, context: &Context) -> GradientItem { - match *self { - specified::GradientItem::ColorStop(ref stop) => { - GradientItem::ColorStop(stop.to_computed_value(context)) - }, - specified::GradientItem::InterpolationHint(ref hint) => { - GradientItem::InterpolationHint(hint.to_computed_value(context)) - }, - } - } - - #[inline] - fn from_computed_value(computed: &GradientItem) -> Self { - match *computed { - GradientItem::ColorStop(ref stop) => { - specified::GradientItem::ColorStop(ToComputedValue::from_computed_value(stop)) - }, - GradientItem::InterpolationHint(ref hint) => { - specified::GradientItem::InterpolationHint(ToComputedValue::from_computed_value(hint)) - }, - } - } -} - -/// Computed values for one color stop in a linear gradient. -/// https://drafts.csswg.org/css-images/#typedef-color-stop-list -#[derive(Clone, PartialEq, Copy)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct ColorStop { - /// The color of this stop. - pub color: CSSColor, - - /// The position of this stop. If not specified, this stop is placed halfway between the - /// point that precedes it and the point that follows it per CSS-IMAGES § 3.4. - pub position: Option, -} - -impl ToCss for ColorStop { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - try!(self.color.to_css(dest)); - if let Some(position) = self.position { - try!(dest.write_str(" ")); - try!(position.to_css(dest)); - } - Ok(()) - } -} - -impl fmt::Debug for ColorStop { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let _ = write!(f, "{:?}", self.color); - self.position.map(|pos| { - let _ = write!(f, " {:?}", pos); - }); - Ok(()) - } -} - -impl ToComputedValue for specified::ColorStop { - type ComputedValue = ColorStop; - - #[inline] - fn to_computed_value(&self, context: &Context) -> ColorStop { - ColorStop { - color: self.color.to_computed_value(context), - position: match self.position { - None => None, - Some(ref value) => Some(value.to_computed_value(context)), - }, - } - } - #[inline] - fn from_computed_value(computed: &ColorStop) -> Self { - specified::ColorStop { - color: ToComputedValue::from_computed_value(&computed.color), - position: match computed.position { - None => None, - Some(value) => Some(ToComputedValue::from_computed_value(&value)), - }, - } - } -} - -/// Computed values for EndingShape -/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-ending-shape -#[derive(Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub enum EndingShape { - Circle(LengthOrKeyword), - Ellipse(LengthOrPercentageOrKeyword), -} - -impl ToCss for EndingShape { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - EndingShape::Circle(ref length) => { - try!(dest.write_str("circle ")); - try!(length.to_css(dest)); - }, - EndingShape::Ellipse(ref length) => { - try!(dest.write_str("ellipse ")); - try!(length.to_css(dest)); - }, - } - Ok(()) - } -} - -impl fmt::Debug for EndingShape { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - EndingShape::Circle(ref length) => { - let _ = write!(f, "circle {:?}", length); - }, - EndingShape::Ellipse(ref length) => { - let _ = write!(f, "ellipse {:?}", length); - } - } - Ok(()) - } -} - -impl ToComputedValue for specified::GradientEndingShape { - type ComputedValue = EndingShape; - - #[inline] - fn to_computed_value(&self, context: &Context) -> EndingShape { - match *self { - specified::GradientEndingShape::Circle(ref length) => { - EndingShape::Circle(length.to_computed_value(context)) - }, - specified::GradientEndingShape::Ellipse(ref length) => { - EndingShape::Ellipse(length.to_computed_value(context)) - }, - } - } - #[inline] - fn from_computed_value(computed: &EndingShape) -> Self { - match *computed { - EndingShape::Circle(ref length) => { - specified::GradientEndingShape::Circle(ToComputedValue::from_computed_value(length)) - }, - EndingShape::Ellipse(ref length) => { - specified::GradientEndingShape::Ellipse(ToComputedValue::from_computed_value(length)) - }, - } - } -} - -/// Computed values for ImageRect -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub struct ImageRect { - pub url: SpecifiedUrl, - pub top: NumberOrPercentage, - pub bottom: NumberOrPercentage, - pub right: NumberOrPercentage, - pub left: NumberOrPercentage, -} - -impl ToCss for ImageRect { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - dest.write_str("-moz-image-rect(")?; - self.url.to_css(dest)?; - dest.write_str(", ")?; - self.top.to_css(dest)?; - dest.write_str(", ")?; - self.right.to_css(dest)?; - dest.write_str(", ")?; - self.bottom.to_css(dest)?; - dest.write_str(", ")?; - self.left.to_css(dest)?; - dest.write_str(")") - } -} - -impl ToComputedValue for specified::ImageRect { - type ComputedValue = ImageRect; - - #[inline] - fn to_computed_value(&self, context: &Context) -> ImageRect { - ImageRect { - url: self.url.to_computed_value(context), - top: self.top.to_computed_value(context), - right: self.right.to_computed_value(context), - bottom: self.bottom.to_computed_value(context), - left: self.left.to_computed_value(context), - } - } - #[inline] - fn from_computed_value(computed: &ImageRect) -> Self { - specified::ImageRect { - url: ToComputedValue::from_computed_value(&computed.url), - top: ToComputedValue::from_computed_value(&computed.top), - right: ToComputedValue::from_computed_value(&computed.right), - bottom: ToComputedValue::from_computed_value(&computed.bottom), - left: ToComputedValue::from_computed_value(&computed.left), - } - } -} - -/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-size -#[derive(Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub enum LengthOrKeyword { - Length(Length), - Keyword(SizeKeyword), -} - -impl ToCss for LengthOrKeyword { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LengthOrKeyword::Length(ref length) => length.to_css(dest), - LengthOrKeyword::Keyword(keyword) => keyword.to_css(dest), - } - } -} - -impl fmt::Debug for LengthOrKeyword { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - LengthOrKeyword::Length(ref length) => { - let _ = write!(f, "{:?}", length); - }, - LengthOrKeyword::Keyword(keyword) => { - let _ = write!(f, "{:?}", keyword); - }, - } - Ok(()) - } -} - -impl ToComputedValue for specified::LengthOrKeyword { - type ComputedValue = LengthOrKeyword; - - #[inline] - fn to_computed_value(&self, context: &Context) -> LengthOrKeyword { - match *self { - specified::LengthOrKeyword::Length(ref length) => { - LengthOrKeyword::Length(length.to_computed_value(context)) - }, - specified::LengthOrKeyword::Keyword(keyword) => { - LengthOrKeyword::Keyword(keyword) - }, - } - } - #[inline] - fn from_computed_value(computed: &LengthOrKeyword) -> Self { - match *computed { - LengthOrKeyword::Length(length) => { - specified::LengthOrKeyword::Length(ToComputedValue::from_computed_value(&length)) - }, - LengthOrKeyword::Keyword(keyword) => { - specified::LengthOrKeyword::Keyword(keyword) - }, - } - } -} - -/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-size -#[derive(Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub enum LengthOrPercentageOrKeyword { - LengthOrPercentage(LengthOrPercentage, LengthOrPercentage), - Keyword(SizeKeyword), -} - -impl ToCss for LengthOrPercentageOrKeyword { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LengthOrPercentageOrKeyword::LengthOrPercentage(ref first_len, second_len) => { - try!(first_len.to_css(dest)); - try!(dest.write_str(" ")); - second_len.to_css(dest) - }, - LengthOrPercentageOrKeyword::Keyword(keyword) => keyword.to_css(dest), - } - } -} - -impl fmt::Debug for LengthOrPercentageOrKeyword { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - LengthOrPercentageOrKeyword::LengthOrPercentage(ref first_len, second_len) => { - let _ = write!(f, "{:?} {:?}", first_len, second_len); - }, - LengthOrPercentageOrKeyword::Keyword(keyword) => { - let _ = write!(f, "{:?}", keyword); - }, - } - Ok(()) - } -} - -impl ToComputedValue for specified::LengthOrPercentageOrKeyword { - type ComputedValue = LengthOrPercentageOrKeyword; - - #[inline] - fn to_computed_value(&self, context: &Context) -> LengthOrPercentageOrKeyword { - match *self { - specified::LengthOrPercentageOrKeyword::LengthOrPercentage(ref first_len, ref second_len) => { - LengthOrPercentageOrKeyword::LengthOrPercentage(first_len.to_computed_value(context), - second_len.to_computed_value(context)) - }, - specified::LengthOrPercentageOrKeyword::Keyword(keyword) => { - LengthOrPercentageOrKeyword::Keyword(keyword) - }, - } - } - #[inline] - fn from_computed_value(computed: &LengthOrPercentageOrKeyword) -> Self { - match *computed { - LengthOrPercentageOrKeyword::LengthOrPercentage(first_len, second_len) => { - specified::LengthOrPercentageOrKeyword::LengthOrPercentage( - ToComputedValue::from_computed_value(&first_len), - ToComputedValue::from_computed_value(&second_len)) - }, - LengthOrPercentageOrKeyword::Keyword(keyword) => { - specified::LengthOrPercentageOrKeyword::Keyword(keyword) - }, - } - } -} +/// A computed gradient kind. +pub type GradientKind = GenericGradientKind< + LineDirection, + Length, + LengthOrPercentage, + Position, +>; +/// A computed gradient line direction. #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub enum AngleOrCorner { +pub enum LineDirection { + /// An angle. Angle(Angle), - Corner(HorizontalDirection, VerticalDirection) + /// A corner. + Corner(X, Y), } -impl ToComputedValue for specified::AngleOrCorner { - type ComputedValue = AngleOrCorner; +/// A computed radial gradient ending shape. +pub type EndingShape = GenericEndingShape; - #[inline] - fn to_computed_value(&self, context: &Context) -> AngleOrCorner { +/// A computed gradient item. +pub type GradientItem = GenericGradientItem; + +/// A computed color stop. +pub type ColorStop = GenericColorStop; + +/// Computed values for ImageRect. +pub type ImageRect = GenericImageRect; + +impl GenericLineDirection for LineDirection { + fn points_downwards(&self) -> bool { match *self { - specified::AngleOrCorner::None => { - AngleOrCorner::Angle(Angle::from_radians(PI)) - }, - specified::AngleOrCorner::Angle(angle) => { - AngleOrCorner::Angle(angle.to_computed_value(context)) - }, - specified::AngleOrCorner::Corner(horizontal, vertical) => { - match (horizontal, vertical) { - (None, Some(VerticalDirection::Top)) => { - AngleOrCorner::Angle(Angle::from_radians(0.0)) - }, - (Some(HorizontalDirection::Right), None) => { - AngleOrCorner::Angle(Angle::from_radians(PI * 0.5)) - }, - (None, Some(VerticalDirection::Bottom)) => { - AngleOrCorner::Angle(Angle::from_radians(PI)) - }, - (Some(HorizontalDirection::Left), None) => { - AngleOrCorner::Angle(Angle::from_radians(PI * 1.5)) - }, - (Some(horizontal), Some(vertical)) => { - AngleOrCorner::Corner(horizontal, vertical) - }, - (None, None) => { - unreachable!() - } - } - } + LineDirection::Angle(angle) => angle.radians() == PI, + LineDirection::Corner(..) => false, } } - #[inline] - fn from_computed_value(computed: &AngleOrCorner) -> Self { + fn to_css(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result + where W: fmt::Write + { + match *self { + LineDirection::Angle(ref angle) => angle.to_css(dest), + LineDirection::Corner(x, y) => { + if compat_mode == CompatMode::Modern { + dest.write_str("to ")?; + } + x.to_css(dest)?; + dest.write_str(" ")?; + y.to_css(dest) + }, + } + } +} + +impl ToComputedValue for SpecifiedLineDirection { + type ComputedValue = LineDirection; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + SpecifiedLineDirection::Angle(ref angle) => { + LineDirection::Angle(angle.to_computed_value(context)) + }, + SpecifiedLineDirection::Horizontal(X::Left) => { + LineDirection::Angle(Angle::Degree(270.)) + }, + SpecifiedLineDirection::Horizontal(X::Right) => { + LineDirection::Angle(Angle::Degree(90.)) + }, + SpecifiedLineDirection::Vertical(Y::Top) => { + LineDirection::Angle(Angle::Degree(0.)) + }, + SpecifiedLineDirection::Vertical(Y::Bottom) => { + LineDirection::Angle(Angle::Degree(180.)) + }, + SpecifiedLineDirection::Corner(x, y) => { + LineDirection::Corner(x, y) + }, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { match *computed { - AngleOrCorner::Angle(ref angle) => { - specified::AngleOrCorner::Angle(specified::Angle::from_computed_value(angle)) + LineDirection::Angle(ref angle) => { + SpecifiedLineDirection::Angle(ToComputedValue::from_computed_value(angle)) + }, + LineDirection::Corner(x, y) => { + SpecifiedLineDirection::Corner(x, y) }, - AngleOrCorner::Corner(horizontal, vertical) => { - specified::AngleOrCorner::Corner(Some(horizontal), Some(vertical)) - } - } - } -} - -impl AngleOrCorner { - fn to_css(&self, dest: &mut W, mode: CompatMode) -> fmt::Result where W: fmt::Write { - match *self { - AngleOrCorner::Angle(angle) => angle.to_css(dest), - AngleOrCorner::Corner(horizontal, vertical) => { - if mode == CompatMode::Modern { - try!(dest.write_str("to ")); - } - try!(horizontal.to_css(dest)); - try!(dest.write_str(" ")); - try!(vertical.to_css(dest)); - Ok(()) - } - } - } -} - -/// Computed values for none | | . -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct LayerImage(pub Option); - -impl ToCss for LayerImage { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match self.0 { - None => dest.write_str("none"), - Some(ref image) => image.to_css(dest), } } } diff --git a/servo/components/style/values/computed/length.rs b/servo/components/style/values/computed/length.rs index 5addd13be6d1..1bab68160bef 100644 --- a/servo/components/style/values/computed/length.rs +++ b/servo/components/style/values/computed/length.rs @@ -12,8 +12,7 @@ use super::{Number, ToComputedValue, Context}; use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified}; use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength, ViewportPercentageLength}; -pub use super::image::{EndingShape as GradientShape, Gradient, GradientKind, Image}; -pub use super::image::{LengthOrKeyword, LengthOrPercentageOrKeyword}; +pub use super::image::Image; pub use values::specified::{Angle, BorderStyle, Time, UrlOrNone}; impl ToComputedValue for specified::NoCalcLength { diff --git a/servo/components/style/values/computed/mod.rs b/servo/components/style/values/computed/mod.rs index f1721e552cdc..02a71c6b313c 100644 --- a/servo/components/style/values/computed/mod.rs +++ b/servo/components/style/values/computed/mod.rs @@ -22,8 +22,7 @@ use super::specified::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as G pub use app_units::Au; pub use cssparser::Color as CSSColor; -pub use self::image::{AngleOrCorner, EndingShape as GradientShape, Gradient, GradientItem, LayerImage}; -pub use self::image::{GradientKind, Image, ImageRect, LengthOrKeyword, LengthOrPercentageOrKeyword}; +pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect}; pub use super::{Auto, Either, None_}; #[cfg(feature = "gecko")] pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; diff --git a/servo/components/style/values/generics/image.rs b/servo/components/style/values/generics/image.rs new file mode 100644 index 000000000000..55bb8d20d43e --- /dev/null +++ b/servo/components/style/values/generics/image.rs @@ -0,0 +1,636 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Generic types for the handling of [images]. +//! +//! [images]: https://drafts.csswg.org/css-images/#image-values + +use Atom; +use cssparser::serialize_identifier; +use std::fmt; +use style_traits::ToCss; +use values::HasViewportPercentage; +use values::computed::{Context, ToComputedValue}; +use values::specified::url::SpecifiedUrl; + +/// An [image]. +/// +/// [image]: https://drafts.csswg.org/css-images/#image-values +#[derive(Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum Image { + /// A `` image. + Url(SpecifiedUrl), + /// A `` image. + Gradient(Gradient), + /// A `-moz-image-rect` image + Rect(ImageRect), + /// A `-moz-element(# )` + Element(Atom), +} + +/// A CSS gradient. +/// https://drafts.csswg.org/css-images/#gradients +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct Gradient { + /// Gradients can be linear or radial. + pub kind: GradientKind, + /// The color stops and interpolation hints. + pub items: Vec>, + /// True if this is a repeating gradient. + pub repeating: bool, + /// Compatibility mode. + pub compat_mode: CompatMode, +} + +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +/// Whether we used the modern notation or the compatibility `-webkit` prefix. +pub enum CompatMode { + /// Modern syntax. + Modern, + /// `-webkit` prefix. + WebKit, +} + +/// A gradient kind. +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum GradientKind { + /// A linear gradient. + Linear(LineDirection), + /// A radial gradient. + Radial(EndingShape, Position), +} + +/// A radial gradient's ending shape. +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum EndingShape { + /// A circular gradient. + Circle(Circle), + /// An elliptic gradient. + Ellipse(Ellipse), +} + +/// A circle shape. +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum Circle { + /// A circle radius. + Radius(Length), + /// A circle extent. + Extent(ShapeExtent), +} + +/// An ellipse shape. +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum Ellipse { + /// An ellipse pair of radii. + Radii(LengthOrPercentage, LengthOrPercentage), + /// An ellipse extent. + Extent(ShapeExtent), +} + +/// https://drafts.csswg.org/css-images/#typedef-extent-keyword +define_css_keyword_enum!(ShapeExtent: + "closest-side" => ClosestSide, + "farthest-side" => FarthestSide, + "closest-corner" => ClosestCorner, + "farthest-corner" => FarthestCorner, + "contain" => Contain, + "cover" => Cover +); + +/// A gradient item. +/// https://drafts.csswg.org/css-images-4/#color-stop-syntax +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum GradientItem { + /// A color stop. + ColorStop(ColorStop), + /// An interpolation hint. + InterpolationHint(LengthOrPercentage), +} + +/// A color stop. +/// https://drafts.csswg.org/css-images/#typedef-color-stop-list +#[derive(Clone, Copy, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct ColorStop { + /// The color of this stop. + pub color: Color, + /// The position of this stop. + pub position: Option, +} + +/// Values for `moz-image-rect`. +/// +/// `-moz-image-rect(, top, right, bottom, left);` +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[allow(missing_docs)] +pub struct ImageRect { + pub url: SpecifiedUrl, + pub top: NumberOrPercentage, + pub bottom: NumberOrPercentage, + pub right: NumberOrPercentage, + pub left: NumberOrPercentage, +} + +impl fmt::Debug for Image + where G: fmt::Debug, R: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Image::Url(ref url) => url.to_css(f), + Image::Gradient(ref grad) => grad.fmt(f), + Image::Rect(ref rect) => rect.fmt(f), + Image::Element(ref selector) => { + f.write_str("-moz-element(#")?; + serialize_identifier(&selector.to_string(), f)?; + f.write_str(")") + }, + } + } +} + +impl ToCss for Image + where G: ToCss, R: ToCss, +{ + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + Image::Url(ref url) => url.to_css(dest), + Image::Gradient(ref gradient) => gradient.to_css(dest), + Image::Rect(ref rect) => rect.to_css(dest), + Image::Element(ref selector) => { + dest.write_str("-moz-element(#")?; + serialize_identifier(&selector.to_string(), dest)?; + dest.write_str(")") + }, + } + } +} + +impl HasViewportPercentage for Image + where G: HasViewportPercentage +{ + fn has_viewport_percentage(&self) -> bool { + match *self { + Image::Gradient(ref gradient) => gradient.has_viewport_percentage(), + _ => false, + } + } +} + +impl ToComputedValue for Image + where G: ToComputedValue, R: ToComputedValue, +{ + type ComputedValue = Image<::ComputedValue, + ::ComputedValue>; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + Image::Url(ref url) => { + Image::Url(url.clone()) + }, + Image::Gradient(ref gradient) => { + Image::Gradient(gradient.to_computed_value(context)) + }, + Image::Rect(ref rect) => { + Image::Rect(rect.to_computed_value(context)) + }, + Image::Element(ref selector) => { + Image::Element(selector.clone()) + } + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + Image::Url(ref url) => { + Image::Url(url.clone()) + }, + Image::Gradient(ref gradient) => { + Image::Gradient(ToComputedValue::from_computed_value(gradient)) + }, + Image::Rect(ref rect) => { + Image::Rect(ToComputedValue::from_computed_value(rect)) + }, + Image::Element(ref selector) => { + Image::Element(selector.clone()) + }, + } + } +} + +impl ToCss for Gradient + where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss, +{ + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + if self.compat_mode == CompatMode::WebKit { + dest.write_str("-webkit-")?; + } + if self.repeating { + dest.write_str("repeating-")?; + } + dest.write_str(self.kind.label())?; + dest.write_str("-gradient(")?; + let mut skip_comma = match self.kind { + GradientKind::Linear(ref direction) if direction.points_downwards() => true, + GradientKind::Linear(ref direction) => { + direction.to_css(dest, self.compat_mode)?; + false + }, + GradientKind::Radial(ref shape, ref position) => { + let omit_shape = match *shape { + EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) | + EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => { + true + }, + _ => false, + }; + if self.compat_mode == CompatMode::Modern { + if !omit_shape { + shape.to_css(dest)?; + dest.write_str(" ")?; + } + dest.write_str("at ")?; + position.to_css(dest)?; + } else { + position.to_css(dest)?; + if !omit_shape { + dest.write_str(", ")?; + shape.to_css(dest)?; + } + } + false + }, + }; + for item in &self.items { + if !skip_comma { + dest.write_str(", ")?; + } + skip_comma = false; + item.to_css(dest)?; + } + dest.write_str(")") + } +} + +impl HasViewportPercentage for Gradient + where L: HasViewportPercentage, + LoP: HasViewportPercentage, + P: HasViewportPercentage, +{ + fn has_viewport_percentage(&self) -> bool { + self.kind.has_viewport_percentage() || + self.items.iter().any(|i| i.has_viewport_percentage()) + } +} + +impl ToComputedValue for Gradient + where D: ToComputedValue, + L: ToComputedValue, + LoP: ToComputedValue, + P: ToComputedValue, + C: ToComputedValue, +{ + type ComputedValue = Gradient<::ComputedValue, + ::ComputedValue, + ::ComputedValue, +

::ComputedValue, + ::ComputedValue>; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + Gradient { + kind: self.kind.to_computed_value(context), + items: self.items.iter().map(|s| s.to_computed_value(context)).collect(), + repeating: self.repeating, + compat_mode: self.compat_mode, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + Gradient { + kind: ToComputedValue::from_computed_value(&computed.kind), + items: computed.items.iter().map(ToComputedValue::from_computed_value).collect(), + repeating: computed.repeating, + compat_mode: computed.compat_mode, + } + } +} + +impl GradientKind { + fn label(&self) -> &str { + match *self { + GradientKind::Linear(..) => "linear", + GradientKind::Radial(..) => "radial", + } + } +} + +impl HasViewportPercentage for GradientKind + where L: HasViewportPercentage, + LoP: HasViewportPercentage, + P: HasViewportPercentage +{ + fn has_viewport_percentage(&self) -> bool { + match *self { + GradientKind::Linear(_) => false, + GradientKind::Radial(ref shape, ref position) => { + shape.has_viewport_percentage() || position.has_viewport_percentage() + }, + } + } +} + +impl ToComputedValue for GradientKind + where D: ToComputedValue, + L: ToComputedValue, + LoP: ToComputedValue, + P: ToComputedValue, +{ + type ComputedValue = GradientKind<::ComputedValue, + ::ComputedValue, + ::ComputedValue, +

::ComputedValue>; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + GradientKind::Linear(ref direction) => { + GradientKind::Linear(direction.to_computed_value(context)) + }, + GradientKind::Radial(ref shape, ref position) => { + GradientKind::Radial(shape.to_computed_value(context), position.to_computed_value(context)) + }, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + GradientKind::Linear(ref direction) => { + GradientKind::Linear(ToComputedValue::from_computed_value(direction)) + }, + GradientKind::Radial(ref shape, ref position) => { + GradientKind::Radial( + ToComputedValue::from_computed_value(shape), + ToComputedValue::from_computed_value(position), + ) + } + } + } +} + +/// The direction of a linear gradient. +pub trait LineDirection { + /// Whether this direction points towards, and thus can be omitted. + fn points_downwards(&self) -> bool; + + /// Serialises this direction according to the compatibility mode. + fn to_css(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result + where W: fmt::Write; +} + +impl ToCss for EndingShape + where L: ToCss, LoP: ToCss, +{ + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + EndingShape::Circle(Circle::Extent(ShapeExtent::FarthestCorner)) | + EndingShape::Circle(Circle::Extent(ShapeExtent::Cover)) => { + dest.write_str("circle") + }, + EndingShape::Circle(Circle::Extent(keyword)) => { + dest.write_str("circle ")?; + keyword.to_css(dest) + }, + EndingShape::Circle(Circle::Radius(ref length)) => { + length.to_css(dest) + }, + EndingShape::Ellipse(Ellipse::Extent(keyword)) => { + keyword.to_css(dest) + }, + EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => { + x.to_css(dest)?; + dest.write_str(" ")?; + y.to_css(dest) + }, + } + } +} + +impl HasViewportPercentage for EndingShape + where L: HasViewportPercentage, LoP: HasViewportPercentage, +{ + fn has_viewport_percentage(&self) -> bool { + match *self { + EndingShape::Circle(Circle::Radius(ref length)) => { + length.has_viewport_percentage() + }, + EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => { + x.has_viewport_percentage() || y.has_viewport_percentage() + }, + _ => false, + } + } +} + +impl ToComputedValue for EndingShape + where L: ToComputedValue, LoP: ToComputedValue, +{ + type ComputedValue = EndingShape<::ComputedValue, + ::ComputedValue>; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + EndingShape::Circle(Circle::Radius(ref length)) => { + EndingShape::Circle(Circle::Radius(length.to_computed_value(context))) + }, + EndingShape::Circle(Circle::Extent(extent)) => { + EndingShape::Circle(Circle::Extent(extent)) + }, + EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => { + EndingShape::Ellipse(Ellipse::Radii( + x.to_computed_value(context), + y.to_computed_value(context), + )) + }, + EndingShape::Ellipse(Ellipse::Extent(extent)) => { + EndingShape::Ellipse(Ellipse::Extent(extent)) + }, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + EndingShape::Circle(Circle::Radius(ref length)) => { + EndingShape::Circle(Circle::Radius(ToComputedValue::from_computed_value(length))) + }, + EndingShape::Circle(Circle::Extent(extent)) => { + EndingShape::Circle(Circle::Extent(extent)) + }, + EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => { + EndingShape::Ellipse(Ellipse::Radii( + ToComputedValue::from_computed_value(x), + ToComputedValue::from_computed_value(y), + )) + }, + EndingShape::Ellipse(Ellipse::Extent(extent)) => { + EndingShape::Ellipse(Ellipse::Extent(extent)) + }, + } + } +} + +impl ToCss for GradientItem + where C: ToCss, L: ToCss, +{ + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + GradientItem::ColorStop(ref stop) => stop.to_css(dest), + GradientItem::InterpolationHint(ref hint) => hint.to_css(dest), + } + } +} + +impl HasViewportPercentage for GradientItem + where L: HasViewportPercentage, +{ + fn has_viewport_percentage(&self) -> bool { + match *self { + GradientItem::ColorStop(ref stop) => stop.has_viewport_percentage(), + GradientItem::InterpolationHint(ref hint) => hint.has_viewport_percentage(), + } + } +} + +impl ToComputedValue for GradientItem + where C: ToComputedValue, L: ToComputedValue, +{ + type ComputedValue = GradientItem<::ComputedValue, + ::ComputedValue>; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + GradientItem::ColorStop(ref stop) => { + GradientItem::ColorStop(stop.to_computed_value(context)) + }, + GradientItem::InterpolationHint(ref hint) => { + GradientItem::InterpolationHint(hint.to_computed_value(context)) + }, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + GradientItem::ColorStop(ref stop) => { + GradientItem::ColorStop(ToComputedValue::from_computed_value(stop)) + }, + GradientItem::InterpolationHint(ref hint) => { + GradientItem::InterpolationHint(ToComputedValue::from_computed_value(hint)) + }, + } + } +} + +impl fmt::Debug for ColorStop + where C: fmt::Debug, L: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.color)?; + if let Some(ref pos) = self.position { + write!(f, " {:?}", pos)?; + } + Ok(()) + } +} + +impl ToCss for ColorStop + where C: ToCss, L: ToCss, +{ + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + self.color.to_css(dest)?; + if let Some(ref position) = self.position { + dest.write_str(" ")?; + position.to_css(dest)?; + } + Ok(()) + } +} + +impl HasViewportPercentage for ColorStop + where L: HasViewportPercentage, +{ + fn has_viewport_percentage(&self) -> bool { + self.position.as_ref().map_or(false, HasViewportPercentage::has_viewport_percentage) + } +} + +impl ToComputedValue for ColorStop + where C: ToComputedValue, L: ToComputedValue, +{ + type ComputedValue = ColorStop<::ComputedValue, + ::ComputedValue>; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + ColorStop { + color: self.color.to_computed_value(context), + position: self.position.as_ref().map(|p| p.to_computed_value(context)), + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + ColorStop { + color: ToComputedValue::from_computed_value(&computed.color), + position: computed.position.as_ref().map(ToComputedValue::from_computed_value), + } + } +} + +impl ToCss for ImageRect + where C: ToCss, +{ + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + dest.write_str("-moz-image-rect(")?; + self.url.to_css(dest)?; + dest.write_str(", ")?; + self.top.to_css(dest)?; + dest.write_str(", ")?; + self.right.to_css(dest)?; + dest.write_str(", ")?; + self.bottom.to_css(dest)?; + dest.write_str(", ")?; + self.left.to_css(dest)?; + dest.write_str(")") + } +} + +impl ToComputedValue for ImageRect + where C: ToComputedValue, +{ + type ComputedValue = ImageRect<::ComputedValue>; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + ImageRect { + url: self.url.to_computed_value(context), + top: self.top.to_computed_value(context), + right: self.right.to_computed_value(context), + bottom: self.bottom.to_computed_value(context), + left: self.left.to_computed_value(context), + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + ImageRect { + url: ToComputedValue::from_computed_value(&computed.url), + top: ToComputedValue::from_computed_value(&computed.top), + right: ToComputedValue::from_computed_value(&computed.right), + bottom: ToComputedValue::from_computed_value(&computed.bottom), + left: ToComputedValue::from_computed_value(&computed.left), + } + } +} diff --git a/servo/components/style/values/generics/mod.rs b/servo/components/style/values/generics/mod.rs index 43cf722fb7e2..604d6e21238e 100644 --- a/servo/components/style/values/generics/mod.rs +++ b/servo/components/style/values/generics/mod.rs @@ -14,6 +14,7 @@ use super::computed::{Context, ToComputedValue}; pub use self::basic_shape::serialize_radius_values; pub mod basic_shape; +pub mod image; pub mod position; #[derive(Clone, PartialEq, Debug)] diff --git a/servo/components/style/values/specified/image.rs b/servo/components/style/values/specified/image.rs index 515082d2c118..dcbc5db1c5c3 100644 --- a/servo/components/style/values/specified/image.rs +++ b/servo/components/style/values/specified/image.rs @@ -8,212 +8,634 @@ //! [image]: https://drafts.csswg.org/css-images/#image-values use Atom; -use cssparser::{Parser, Token, serialize_identifier}; +use cssparser::{Parser, Token}; use parser::{Parse, ParserContext}; #[cfg(feature = "servo")] use servo_url::ServoUrl; +use std::cmp::Ordering; +use std::f32::consts::PI; use std::fmt; use style_traits::ToCss; -use values::specified::{Angle, CSSColor, Length, LengthOrPercentage, NumberOrPercentage}; -use values::specified::position::Position; +use values::{Either, None_}; +use values::generics::image::{Circle, CompatMode, Ellipse, ColorStop as GenericColorStop}; +use values::generics::image::{EndingShape as GenericEndingShape, Gradient as GenericGradient}; +use values::generics::image::{GradientItem as GenericGradientItem, GradientKind as GenericGradientKind}; +use values::generics::image::{Image as GenericImage, ImageRect as GenericImageRect}; +use values::generics::image::{LineDirection as GenericsLineDirection, ShapeExtent}; +use values::generics::position::Position as GenericPosition; +use values::specified::{Angle, CSSColor, Color, Length, LengthOrPercentage}; +use values::specified::{Number, NumberOrPercentage, Percentage}; +use values::specified::position::{Position, PositionComponent, Side, X, Y}; use values::specified::url::SpecifiedUrl; +/// A specified image layer. +pub type ImageLayer = Either; + /// Specified values for an image according to CSS-IMAGES. /// https://drafts.csswg.org/css-images/#image-values -#[derive(Clone, PartialEq, Debug)] +pub type Image = GenericImage; + +/// Specified values for a CSS gradient. +/// https://drafts.csswg.org/css-images/#gradients +pub type Gradient = GenericGradient< + LineDirection, + Length, + LengthOrPercentage, + Position, + CSSColor, +>; + +/// A specified gradient kind. +pub type GradientKind = GenericGradientKind< + LineDirection, + Length, + LengthOrPercentage, + Position, +>; + +/// A specified gradient line direction. +#[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum Image { - /// A `` image. - Url(SpecifiedUrl), - /// A `` image. - Gradient(Gradient), - /// A `-moz-image-rect` image - ImageRect(ImageRect), - /// A `-moz-element(# )` - Element(Atom), +pub enum LineDirection { + /// An angular direction. + Angle(Angle), + /// A horizontal direction. + Horizontal(X), + /// A vertical direction. + Vertical(Y), + /// A direction towards a corner of a box. + Corner(X, Y), } -impl ToCss for Image { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - Image::Url(ref url_value) => url_value.to_css(dest), - Image::Gradient(ref gradient) => gradient.to_css(dest), - Image::ImageRect(ref image_rect) => image_rect.to_css(dest), - Image::Element(ref selector) => { - dest.write_str("-moz-element(#")?; - // FIXME: We should get rid of these intermediate strings. - serialize_identifier(&*selector.to_string(), dest)?; - dest.write_str(")") - }, +/// A specified ending shape. +pub type EndingShape = GenericEndingShape; + +/// A specified gradient item. +pub type GradientItem = GenericGradientItem; + +/// A computed color stop. +pub type ColorStop = GenericColorStop; + +/// Specified values for `moz-image-rect` +/// -moz-image-rect(, top, right, bottom, left); +pub type ImageRect = GenericImageRect; + +impl Parse for Image { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + if let Ok(url) = input.try(|input| SpecifiedUrl::parse(context, input)) { + return Ok(GenericImage::Url(url)); } + if let Ok(gradient) = input.try(|i| Gradient::parse(context, i)) { + return Ok(GenericImage::Gradient(gradient)); + } + if let Ok(image_rect) = input.try(|input| ImageRect::parse(context, input)) { + return Ok(GenericImage::Rect(image_rect)); + } + + Ok(GenericImage::Element(Image::parse_element(input)?)) } } impl Image { - #[allow(missing_docs)] - pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { - if let Ok(url) = input.try(|input| SpecifiedUrl::parse(context, input)) { - return Ok(Image::Url(url)); - } - if let Ok(gradient) = input.try(|input| Gradient::parse_function(context, input)) { - return Ok(Image::Gradient(gradient)); - } - if let Ok(image_rect) = input.try(|input| ImageRect::parse(context, input)) { - return Ok(Image::ImageRect(image_rect)); - } - - Ok(Image::Element(Image::parse_element(input)?)) - } - /// Creates an already specified image value from an already resolved URL /// for insertion in the cascade. #[cfg(feature = "servo")] pub fn for_cascade(url: ServoUrl) -> Self { - Image::Url(SpecifiedUrl::for_cascade(url)) + GenericImage::Url(SpecifiedUrl::for_cascade(url)) } /// Parses a `-moz-element(# )`. fn parse_element(input: &mut Parser) -> Result { - if input.try(|i| i.expect_function_matching("-moz-element")).is_ok() { - input.parse_nested_block(|i| { - match i.next()? { - Token::IDHash(id) => Ok(Atom::from(id)), - _ => Err(()), - } - }) - } else { - Err(()) - } - } -} - -/// Specified values for a CSS gradient. -/// https://drafts.csswg.org/css-images/#gradients -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct Gradient { - /// The color stops and interpolation hints. - pub items: Vec, - /// True if this is a repeating gradient. - pub repeating: bool, - /// Gradients can be linear or radial. - pub gradient_kind: GradientKind, - /// Compatibility mode. - pub compat_mode: CompatMode, -} - -impl ToCss for Gradient { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - if self.compat_mode == CompatMode::WebKit { - try!(dest.write_str("-webkit-")); - } - if self.repeating { - try!(dest.write_str("repeating-")); - } - let mut skipcomma = false; - match self.gradient_kind { - GradientKind::Linear(angle_or_corner) => { - try!(dest.write_str("linear-gradient(")); - try!(angle_or_corner.to_css(dest, self.compat_mode)); - if angle_or_corner == AngleOrCorner::None { - skipcomma = true; - } - }, - GradientKind::Radial(ref shape, ref position) => { - try!(dest.write_str("radial-gradient(")); - if self.compat_mode == CompatMode::Modern { - try!(shape.to_css(dest)); - try!(dest.write_str(" at ")); - try!(position.to_css(dest)); - } else { - try!(position.to_css(dest)); - try!(dest.write_str(", ")); - try!(shape.to_css(dest)); - } - }, - } - for item in &self.items { - if !skipcomma { - try!(dest.write_str(", ")); - } else { - skipcomma = false; + input.try(|i| i.expect_function_matching("-moz-element"))?; + input.parse_nested_block(|i| { + match i.next()? { + Token::IDHash(id) => Ok(Atom::from(id)), + _ => Err(()), } - try!(item.to_css(dest)); - } - dest.write_str(")") + }) } } -impl Gradient { - /// Parses a gradient from the given arguments. - pub fn parse_function(context: &ParserContext, input: &mut Parser) -> Result { - fn parse(context: &ParserContext, input: &mut Parser, parse_kind: F) - -> Result<(GradientKind, Vec), ()> - where F: FnOnce(&ParserContext, &mut Parser) -> Result - { - input.parse_nested_block(|input| { - let kind = try!(parse_kind(context, input)); - let items = try!(Gradient::parse_items(context, input)); - Ok((kind, items)) - }) - }; - let mut repeating = false; - let mut compat_mode = CompatMode::Modern; - let (gradient_kind, items) = match_ignore_ascii_case! { &try!(input.expect_function()), +impl Parse for Gradient { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + enum Shape { + Linear, + Radial, + } + + let (shape, repeating, compat_mode) = match_ignore_ascii_case! { &try!(input.expect_function()), "linear-gradient" => { - try!(parse(context, input, GradientKind::parse_modern_linear)) + (Shape::Linear, false, CompatMode::Modern) }, "-webkit-linear-gradient" => { - compat_mode = CompatMode::WebKit; - try!(parse(context, input, GradientKind::parse_webkit_linear)) + (Shape::Linear, false, CompatMode::WebKit) }, "repeating-linear-gradient" => { - repeating = true; - try!(parse(context, input, GradientKind::parse_modern_linear)) + (Shape::Linear, true, CompatMode::Modern) }, "-webkit-repeating-linear-gradient" => { - repeating = true; - compat_mode = CompatMode::WebKit; - try!(parse(context, input, GradientKind::parse_webkit_linear)) + (Shape::Linear, true, CompatMode::WebKit) }, "radial-gradient" => { - try!(parse(context, input, GradientKind::parse_modern_radial)) + (Shape::Radial, false, CompatMode::Modern) }, "-webkit-radial-gradient" => { - compat_mode = CompatMode::WebKit; - try!(parse(context, input, GradientKind::parse_webkit_radial)) + (Shape::Radial, false, CompatMode::WebKit) }, "repeating-radial-gradient" => { - repeating = true; - try!(parse(context, input, GradientKind::parse_modern_radial)) + (Shape::Radial, true, CompatMode::Modern) }, "-webkit-repeating-radial-gradient" => { - repeating = true; - compat_mode = CompatMode::WebKit; - try!(parse(context, input, GradientKind::parse_webkit_radial)) + (Shape::Radial, true, CompatMode::WebKit) + }, + "-webkit-gradient" => { + return input.parse_nested_block(|i| Self::parse_webkit_gradient_argument(context, i)); }, _ => { return Err(()); } }; + let (kind, items) = input.parse_nested_block(|i| { + let shape = match shape { + Shape::Linear => GradientKind::parse_linear(context, i, compat_mode)?, + Shape::Radial => GradientKind::parse_radial(context, i, compat_mode)?, + }; + let items = GradientItem::parse_comma_separated(context, i)?; + Ok((shape, items)) + })?; + + if items.len() < 2 { + return Err(()); + } + Ok(Gradient { items: items, repeating: repeating, - gradient_kind: gradient_kind, + kind: kind, compat_mode: compat_mode, }) } +} - fn parse_items(context: &ParserContext, input: &mut Parser) -> Result, ()> { +impl Gradient { + fn parse_webkit_gradient_argument(context: &ParserContext, input: &mut Parser) -> Result { + type Point = GenericPosition, Component>; + + #[derive(Clone, Copy)] + enum Component { + Center, + Number(NumberOrPercentage), + Side(S), + } + + impl LineDirection { + fn from_points(first: Point, second: Point) -> Self { + let h_ord = first.horizontal.partial_cmp(&second.horizontal); + let v_ord = first.vertical.partial_cmp(&second.vertical); + let (h, v) = match (h_ord, v_ord) { + (Some(h), Some(v)) => (h, v), + _ => return LineDirection::Vertical(Y::Bottom), + }; + match (h, v) { + (Ordering::Less, Ordering::Less) => { + LineDirection::Corner(X::Right, Y::Bottom) + }, + (Ordering::Less, Ordering::Equal) => { + LineDirection::Horizontal(X::Right) + }, + (Ordering::Less, Ordering::Greater) => { + LineDirection::Corner(X::Right, Y::Top) + }, + (Ordering::Equal, Ordering::Greater) => { + LineDirection::Vertical(Y::Top) + }, + (Ordering::Equal, Ordering::Equal) | + (Ordering::Equal, Ordering::Less) => { + LineDirection::Vertical(Y::Bottom) + }, + (Ordering::Greater, Ordering::Less) => { + LineDirection::Corner(X::Left, Y::Bottom) + }, + (Ordering::Greater, Ordering::Equal) => { + LineDirection::Horizontal(X::Left) + }, + (Ordering::Greater, Ordering::Greater) => { + LineDirection::Corner(X::Left, Y::Top) + }, + } + } + } + + impl From for Position { + fn from(point: Point) -> Self { + Self::new(point.horizontal.into(), point.vertical.into()) + } + } + + impl Parse for Point { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + input.try(|i| { + let x = Component::parse(context, i)?; + let y = Component::parse(context, i)?; + + Ok(Self::new(x, y)) + }) + } + } + + impl From> for NumberOrPercentage { + fn from(component: Component) -> Self { + match component { + Component::Center => NumberOrPercentage::Percentage(Percentage(0.5)), + Component::Number(number) => number, + Component::Side(side) => { + let p = Percentage(if side.is_start() { 0. } else { 1. }); + NumberOrPercentage::Percentage(p) + }, + } + } + } + + impl From> for PositionComponent { + fn from(component: Component) -> Self { + match component { + Component::Center => { + PositionComponent::Center + }, + Component::Number(NumberOrPercentage::Number(number)) => { + PositionComponent::Length(Length::from_px(number.value).into()) + }, + Component::Number(NumberOrPercentage::Percentage(p)) => { + PositionComponent::Length(p.into()) + }, + Component::Side(side) => { + PositionComponent::Side(side, None) + }, + } + } + } + + impl Component { + fn partial_cmp(&self, other: &Self) -> Option { + match (NumberOrPercentage::from(*self), NumberOrPercentage::from(*other)) { + (NumberOrPercentage::Percentage(a), NumberOrPercentage::Percentage(b)) => { + a.0.partial_cmp(&b.0) + }, + (NumberOrPercentage::Number(a), NumberOrPercentage::Number(b)) => { + a.value.partial_cmp(&b.value) + }, + (_, _) => { + None + } + } + } + } + + impl Parse for Component { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + if let Ok(side) = input.try(|i| S::parse(context, i)) { + return Ok(Component::Side(side)); + } + if let Ok(number) = input.try(|i| NumberOrPercentage::parse(context, i)) { + return Ok(Component::Number(number)); + } + input.try(|i| i.expect_ident_matching("center"))?; + Ok(Component::Center) + } + } + + let ident = input.expect_ident()?; + input.expect_comma()?; + + let (kind, reverse_stops) = match_ignore_ascii_case! { &ident, + "linear" => { + let first = Point::parse(context, input)?; + input.expect_comma()?; + let second = Point::parse(context, input)?; + + let direction = LineDirection::from_points(first, second); + let kind = GenericGradientKind::Linear(direction); + + (kind, false) + }, + "radial" => { + let first_point = Point::parse(context, input)?; + input.expect_comma()?; + let first_radius = Number::parse(context, input)?; + input.expect_comma()?; + let second_point = Point::parse(context, input)?; + input.expect_comma()?; + let second_radius = Number::parse(context, input)?; + + let (reverse_stops, point, radius) = if second_radius.value >= first_radius.value { + (false, second_point, second_radius) + } else { + (true, first_point, first_radius) + }; + + let shape = GenericEndingShape::Circle(Circle::Radius(Length::from_px(radius.value))); + let position = point.into(); + let kind = GenericGradientKind::Radial(shape, position); + + (kind, reverse_stops) + }, + _ => return Err(()), + }; + + let mut items = input.try(|i| { + i.expect_comma()?; + i.parse_comma_separated(|i| { + let function = i.expect_function()?; + let (color, mut p) = i.parse_nested_block(|i| { + let p = match_ignore_ascii_case! { &function, + "color-stop" => { + let p = match NumberOrPercentage::parse(context, i)? { + NumberOrPercentage::Number(number) => number.value, + NumberOrPercentage::Percentage(p) => p.0, + }; + i.expect_comma()?; + p + }, + "from" => 0., + "to" => 1., + _ => return Err(()), + }; + let color = CSSColor::parse(context, i)?; + if color.parsed == Color::CurrentColor { + return Err(()); + } + Ok((color, p)) + })?; + if reverse_stops { + p = 1. - p; + } + Ok(GenericGradientItem::ColorStop(GenericColorStop { + color: color, + position: Some(LengthOrPercentage::Percentage(Percentage(p))), + })) + }) + }).unwrap_or(vec![]); + + if items.is_empty() { + items = vec![ + GenericGradientItem::ColorStop(GenericColorStop { + color: CSSColor::transparent(), + position: Some(Percentage(0.).into()), + }), + GenericGradientItem::ColorStop(GenericColorStop { + color: CSSColor::transparent(), + position: Some(Percentage(1.).into()), + }), + ]; + } else if items.len() == 1 { + let first = items[0].clone(); + items.push(first); + } else { + items.sort_by(|a, b| { + match (a, b) { + (&GenericGradientItem::ColorStop(ref a), &GenericGradientItem::ColorStop(ref b)) => { + match (&a.position, &b.position) { + (&Some(LengthOrPercentage::Percentage(a)), &Some(LengthOrPercentage::Percentage(b))) => { + let ordering = a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal); + if ordering != Ordering::Equal { + return ordering; + } + }, + _ => {}, + } + }, + _ => {}, + } + if reverse_stops { + Ordering::Greater + } else { + Ordering::Less + } + }) + } + + Ok(GenericGradient { + kind: kind, + items: items, + repeating: false, + compat_mode: CompatMode::Modern, + }) + } +} + +impl GradientKind { + fn parse_linear(context: &ParserContext, + input: &mut Parser, + compat_mode: CompatMode) + -> Result { + let direction = if let Ok(d) = input.try(|i| LineDirection::parse(context, i, compat_mode)) { + input.expect_comma()?; + d + } else { + LineDirection::Vertical(Y::Bottom) + }; + Ok(GenericGradientKind::Linear(direction)) + } + + fn parse_radial(context: &ParserContext, + input: &mut Parser, + compat_mode: CompatMode) + -> Result { + let (shape, position) = if compat_mode == CompatMode::Modern { + let shape = input.try(|i| EndingShape::parse(context, i, compat_mode)); + let position = input.try(|i| { + i.expect_ident_matching("at")?; + Position::parse(context, i) + }); + (shape, position) + } else { + let position = input.try(|i| Position::parse(context, i)); + let shape = input.try(|i| { + if position.is_ok() { + i.expect_comma()?; + } + EndingShape::parse(context, i, compat_mode) + }); + (shape, position) + }; + + if shape.is_ok() || position.is_ok() { + input.expect_comma()?; + } + + let shape = shape.unwrap_or({ + GenericEndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) + }); + let position = position.unwrap_or(Position::center()); + Ok(GenericGradientKind::Radial(shape, position)) + } +} + +impl GenericsLineDirection for LineDirection { + fn points_downwards(&self) -> bool { + match *self { + LineDirection::Angle(ref angle) => angle.radians() == PI, + LineDirection::Vertical(Y::Bottom) => true, + _ => false, + } + } + + fn to_css(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result + where W: fmt::Write + { + match *self { + LineDirection::Angle(angle) => { + angle.to_css(dest) + }, + LineDirection::Horizontal(x) => { + if compat_mode == CompatMode::Modern { + dest.write_str("to ")?; + } + x.to_css(dest) + }, + LineDirection::Vertical(y) => { + if compat_mode == CompatMode::Modern { + dest.write_str("to ")?; + } + y.to_css(dest) + }, + LineDirection::Corner(x, y) => { + if compat_mode == CompatMode::Modern { + dest.write_str("to ")?; + } + x.to_css(dest)?; + dest.write_str(" ")?; + y.to_css(dest) + } + } + } +} + +impl LineDirection { + fn parse(context: &ParserContext, + input: &mut Parser, + compat_mode: CompatMode) + -> Result { + if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) { + return Ok(LineDirection::Angle(angle)); + } + input.try(|i| { + if compat_mode == CompatMode::Modern { + i.expect_ident_matching("to")?; + } + if let Ok(x) = i.try(X::parse) { + if let Ok(y) = i.try(Y::parse) { + return Ok(LineDirection::Corner(x, y)); + } + return Ok(LineDirection::Horizontal(x)); + } + let y = Y::parse(i)?; + if let Ok(x) = i.try(X::parse) { + return Ok(LineDirection::Corner(x, y)); + } + Ok(LineDirection::Vertical(y)) + }) + } +} + +impl EndingShape { + fn parse(context: &ParserContext, + input: &mut Parser, + compat_mode: CompatMode) + -> Result { + if let Ok(extent) = input.try(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode)) { + if input.try(|i| i.expect_ident_matching("circle")).is_ok() { + return Ok(GenericEndingShape::Circle(Circle::Extent(extent))); + } + let _ = input.try(|i| i.expect_ident_matching("ellipse")); + return Ok(GenericEndingShape::Ellipse(Ellipse::Extent(extent))); + } + if input.try(|i| i.expect_ident_matching("circle")).is_ok() { + if let Ok(extent) = input.try(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode)) { + return Ok(GenericEndingShape::Circle(Circle::Extent(extent))); + } + if compat_mode == CompatMode::Modern { + if let Ok(length) = input.try(|i| Length::parse(context, i)) { + return Ok(GenericEndingShape::Circle(Circle::Radius(length))); + } + } + return Ok(GenericEndingShape::Circle(Circle::Extent(ShapeExtent::FarthestCorner))); + } + if input.try(|i| i.expect_ident_matching("ellipse")).is_ok() { + if let Ok(extent) = input.try(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode)) { + return Ok(GenericEndingShape::Ellipse(Ellipse::Extent(extent))); + } + if compat_mode == CompatMode::Modern { + let pair: Result<_, ()> = input.try(|i| { + let x = LengthOrPercentage::parse(context, i)?; + let y = LengthOrPercentage::parse(context, i)?; + Ok((x, y)) + }); + if let Ok((x, y)) = pair { + return Ok(GenericEndingShape::Ellipse(Ellipse::Radii(x, y))); + } + } + return Ok(GenericEndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner))); + } + if let Ok(length) = input.try(|i| Length::parse(context, i)) { + if let Ok(y) = input.try(|i| LengthOrPercentage::parse(context, i)) { + if compat_mode == CompatMode::Modern { + let _ = input.try(|i| i.expect_ident_matching("ellipse")); + } + return Ok(GenericEndingShape::Ellipse(Ellipse::Radii(length.into(), y))); + } + if compat_mode == CompatMode::Modern { + let y = input.try(|i| { + i.expect_ident_matching("ellipse")?; + LengthOrPercentage::parse(context, i) + }); + if let Ok(y) = y { + return Ok(GenericEndingShape::Ellipse(Ellipse::Radii(length.into(), y))); + } + let _ = input.try(|i| i.expect_ident_matching("circle")); + } + return Ok(GenericEndingShape::Circle(Circle::Radius(length))); + } + input.try(|i| { + let x = Percentage::parse(context, i)?; + let y = if let Ok(y) = i.try(|i| LengthOrPercentage::parse(context, i)) { + if compat_mode == CompatMode::Modern { + let _ = i.try(|i| i.expect_ident_matching("ellipse")); + } + y + } else { + if compat_mode == CompatMode::Modern { + i.expect_ident_matching("ellipse")?; + } + LengthOrPercentage::parse(context, i)? + }; + Ok(GenericEndingShape::Ellipse(Ellipse::Radii(x.into(), y))) + }) + } +} + +impl ShapeExtent { + fn parse_with_compat_mode(input: &mut Parser, + compat_mode: CompatMode) + -> Result { + match try!(Self::parse(input)) { + ShapeExtent::Contain | ShapeExtent::Cover if compat_mode == CompatMode::Modern => Err(()), + keyword => Ok(keyword), + } + } +} + +impl GradientItem { + fn parse_comma_separated(context: &ParserContext, input: &mut Parser) -> Result, ()> { let mut seen_stop = false; let items = try!(input.parse_comma_separated(|input| { if seen_stop { if let Ok(hint) = input.try(|i| LengthOrPercentage::parse(context, i)) { seen_stop = false; - return Ok(GradientItem::InterpolationHint(hint)); + return Ok(GenericGradientItem::InterpolationHint(hint)); } } seen_stop = true; - ColorStop::parse(context, input).map(GradientItem::ColorStop) + ColorStop::parse(context, input).map(GenericGradientItem::ColorStop) })); if !seen_stop || items.len() < 2 { return Err(()); @@ -222,333 +644,6 @@ impl Gradient { } } -/// Specified values for CSS linear or radial gradients. -/// https://drafts.csswg.org/css-images/#gradients -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum GradientKind { - /// A ``: - /// - /// https://drafts.csswg.org/css-images/#funcdef-linear-gradient - Linear(AngleOrCorner), - - /// A ``: - /// - /// https://drafts.csswg.org/css-images/#radial-gradients - Radial(EndingShape, Position), -} - -#[derive(Clone, Copy, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -/// Whether we used the modern notation or the compatibility `-webkit` prefix. -pub enum CompatMode { - /// Modern syntax. - Modern, - /// `-webkit` prefix. - WebKit, -} - -impl GradientKind { - /// Parses a linear gradient kind from the given arguments. - fn parse_modern_linear(context: &ParserContext, input: &mut Parser) -> Result { - let direction = if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) { - try!(input.expect_comma()); - AngleOrCorner::Angle(angle) - } else { - if input.try(|i| i.expect_ident_matching("to")).is_ok() { - let (horizontal, vertical) = - if let Ok(value) = input.try(HorizontalDirection::parse) { - (Some(value), input.try(VerticalDirection::parse).ok()) - } else { - let value = try!(VerticalDirection::parse(input)); - (input.try(HorizontalDirection::parse).ok(), Some(value)) - }; - try!(input.expect_comma()); - AngleOrCorner::Corner(horizontal, vertical) - } else { - AngleOrCorner::None - } - }; - Ok(GradientKind::Linear(direction)) - } - - fn parse_webkit_linear(context: &ParserContext, input: &mut Parser) -> Result { - let direction = if let Ok(angle) = input.try(|i| Angle::parse_with_unitless(context, i)) { - AngleOrCorner::Angle(angle) - } else { - if let Ok(value) = input.try(HorizontalDirection::parse) { - AngleOrCorner::Corner(Some(value), input.try(VerticalDirection::parse).ok()) - } else { - if let Ok(value) = input.try(VerticalDirection::parse) { - AngleOrCorner::Corner(input.try(HorizontalDirection::parse).ok(), Some(value)) - } else { - AngleOrCorner::None - } - } - }; - if direction != AngleOrCorner::None { - try!(input.expect_comma()); - } - Ok(GradientKind::Linear(direction)) - } - - /// Parses a modern radial gradient from the given arguments. - pub fn parse_modern_radial(context: &ParserContext, input: &mut Parser) -> Result { - let mut needs_comma = true; - - // Ending shape and position can be in various order. Checks all probabilities. - let (shape, position) = if let Ok(position) = input.try(|i| parse_position(context, i)) { - // Handle just - (EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)), position) - } else if let Ok((first, second)) = input.try(|i| parse_two_length(context, i)) { - // Handle ? ? - let _ = input.try(|input| input.expect_ident_matching("ellipse")); - (EndingShape::Ellipse(LengthOrPercentageOrKeyword::LengthOrPercentage(first, second)), - input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) - } else if let Ok(length) = input.try(|i| Length::parse(context, i)) { - // Handle ? ? - let _ = input.try(|input| input.expect_ident_matching("circle")); - (EndingShape::Circle(LengthOrKeyword::Length(length)), - input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) - } else if let Ok(keyword) = input.try(SizeKeyword::parse_modern) { - // Handle ? ? - let shape = if input.try(|input| input.expect_ident_matching("circle")).is_ok() { - EndingShape::Circle(LengthOrKeyword::Keyword(keyword)) - } else { - let _ = input.try(|input| input.expect_ident_matching("ellipse")); - EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(keyword)) - }; - (shape, input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) - } else { - // Handle ? ? - if input.try(|input| input.expect_ident_matching("ellipse")).is_ok() { - // Handle ? ? - let length = input.try(|i| LengthOrPercentageOrKeyword::parse(context, i, SizeKeyword::parse_modern)) - .unwrap_or(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)); - (EndingShape::Ellipse(length), - input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) - } else if input.try(|input| input.expect_ident_matching("circle")).is_ok() { - // Handle ? ? - let length = input.try(|i| LengthOrKeyword::parse(context, i, SizeKeyword::parse_modern)) - .unwrap_or(LengthOrKeyword::Keyword(SizeKeyword::FarthestCorner)); - (EndingShape::Circle(length), input.try(|i| parse_position(context, i)) - .unwrap_or(Position::center())) - } else { - // If there is no shape keyword, it should set to default. - needs_comma = false; - (EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestCorner)), - input.try(|i| parse_position(context, i)).unwrap_or(Position::center())) - } - }; - - if needs_comma { - try!(input.expect_comma()); - } - - Ok(GradientKind::Radial(shape, position)) - } - - /// Parses a webkit radial gradient from the given arguments. - /// https://compat.spec.whatwg.org/#css-gradients-webkit-radial-gradient - pub fn parse_webkit_radial(context: &ParserContext, input: &mut Parser) -> Result { - let position = if let Ok(position) = input.try(|i| Position::parse(context, i)) { - try!(input.expect_comma()); - position - } else { - Position::center() - }; - - let mut needs_comma = true; - - // Ending shape and position can be in various order. Checks all probabilities. - let shape = if let Ok((first, second)) = input.try(|i| parse_two_length(context, i)) { - EndingShape::Ellipse(LengthOrPercentageOrKeyword::LengthOrPercentage(first, second)) - } else if let Ok(keyword) = input.try(SizeKeyword::parse) { - // Handle ? - if input.try(|input| input.expect_ident_matching("circle")).is_ok() { - EndingShape::Circle(LengthOrKeyword::Keyword(keyword)) - } else { - let _ = input.try(|input| input.expect_ident_matching("ellipse")); - EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(keyword)) - } - } else { - // Handle ? - if input.try(|input| input.expect_ident_matching("ellipse")).is_ok() { - // Handle ? - let keyword = input.try(SizeKeyword::parse).unwrap_or((SizeKeyword::Cover)); - EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(keyword)) - } else if input.try(|input| input.expect_ident_matching("circle")).is_ok() { - // Handle ? - let keyword = input.try(SizeKeyword::parse).unwrap_or((SizeKeyword::Cover)); - EndingShape::Circle(LengthOrKeyword::Keyword(keyword)) - } else { - // If there is no shape keyword, it should set to default. - needs_comma = false; - EndingShape::Ellipse(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::Cover)) - } - }; - - if needs_comma { - try!(input.expect_comma()); - } - - Ok(GradientKind::Radial(shape, position)) - } -} - -/// Specified values for `moz-image-rect` -/// -moz-image-rect(, top, right, bottom, left); -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub struct ImageRect { - pub url: SpecifiedUrl, - pub top: NumberOrPercentage, - pub bottom: NumberOrPercentage, - pub right: NumberOrPercentage, - pub left: NumberOrPercentage, -} - -impl ToCss for ImageRect { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - dest.write_str("-moz-image-rect(")?; - self.url.to_css(dest)?; - dest.write_str(", ")?; - self.top.to_css(dest)?; - dest.write_str(", ")?; - self.right.to_css(dest)?; - dest.write_str(", ")?; - self.bottom.to_css(dest)?; - dest.write_str(", ")?; - self.left.to_css(dest)?; - dest.write_str(")") - } -} - -impl Parse for ImageRect { - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - match_ignore_ascii_case! { &try!(input.expect_function()), - "-moz-image-rect" => { - input.parse_nested_block(|input| { - let url = SpecifiedUrl::parse(context, input)?; - input.expect_comma()?; - let top = NumberOrPercentage::parse(context, input)?; - input.expect_comma()?; - let right = NumberOrPercentage::parse(context, input)?; - input.expect_comma()?; - let bottom = NumberOrPercentage::parse(context, input)?; - input.expect_comma()?; - let left = NumberOrPercentage::parse(context, input)?; - - Ok(ImageRect { - url: url, - top: top, - right: right, - bottom: bottom, - left: left, - }) - }) - } - _ => Err(()) - } - } -} - -fn parse_two_length(context: &ParserContext, input: &mut Parser) - -> Result<(LengthOrPercentage, LengthOrPercentage), ()> { - let first = try!(LengthOrPercentage::parse(context, input)); - let second = try!(LengthOrPercentage::parse(context, input)); - Ok((first, second)) -} - -fn parse_position(context: &ParserContext, input: &mut Parser) -> Result { - try!(input.expect_ident_matching("at")); - input.try(|i| Position::parse(context, i)) -} - -/// Specified values for an angle or a corner in a linear gradient. -#[derive(Clone, PartialEq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub enum AngleOrCorner { - Angle(Angle), - Corner(Option, Option), - None, -} - -impl AngleOrCorner { - fn to_css(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result where W: fmt::Write { - match *self { - AngleOrCorner::None => Ok(()), - AngleOrCorner::Angle(angle) => angle.to_css(dest), - AngleOrCorner::Corner(horizontal, vertical) => { - if compat_mode == CompatMode::Modern { - try!(dest.write_str("to ")); - } - let mut horizontal_present = false; - if let Some(horizontal) = horizontal { - try!(horizontal.to_css(dest)); - horizontal_present = true; - } - if let Some(vertical) = vertical { - if horizontal_present { - try!(dest.write_str(" ")); - } - try!(vertical.to_css(dest)); - } - Ok(()) - } - } - } -} - -/// Specified values for color stops and interpolation hints. -/// https://drafts.csswg.org/css-images-4/#color-stop-syntax -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum GradientItem { - /// A color stop. - ColorStop(ColorStop), - /// An interpolation hint. - InterpolationHint(LengthOrPercentage), -} - -impl ToCss for GradientItem { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - GradientItem::ColorStop(ref stop) => stop.to_css(dest), - GradientItem::InterpolationHint(ref hint) => hint.to_css(dest), - } - } -} - -/// Specified values for one color stop in a gradient. -/// https://drafts.csswg.org/css-images/#typedef-color-stop-list -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct ColorStop { - /// The color of this stop. - pub color: CSSColor, - - /// The position of this stop. If not specified, this stop is placed halfway between the - /// point that precedes it and the point that follows it. - pub position: Option, -} - -impl ToCss for ColorStop { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - try!(self.color.to_css(dest)); - if let Some(ref position) = self.position { - try!(dest.write_str(" ")); - try!(position.to_css(dest)); - } - Ok(()) - } -} - -define_css_keyword_enum!(HorizontalDirection: "left" => Left, "right" => Right); -define_css_keyword_enum!(VerticalDirection: "top" => Top, "bottom" => Bottom); - impl Parse for ColorStop { fn parse(context: &ParserContext, input: &mut Parser) -> Result { Ok(ColorStop { @@ -558,159 +653,28 @@ impl Parse for ColorStop { } } -/// Determines whether the gradient's ending shape is a circle or an ellipse. -/// If is omitted, the ending shape defaults to a circle -/// if the is a single , and to an ellipse otherwise. -/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-ending-shape -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub enum EndingShape { - Circle(LengthOrKeyword), - Ellipse(LengthOrPercentageOrKeyword), -} - -impl ToCss for EndingShape { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - EndingShape::Circle(ref length) => { - try!(dest.write_str("circle ")); - try!(length.to_css(dest)); - }, - EndingShape::Ellipse(ref length) => { - try!(dest.write_str("ellipse ")); - try!(length.to_css(dest)); - }, - } - Ok(()) - } -} - -/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-size -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub enum LengthOrKeyword { - Length(Length), - Keyword(SizeKeyword), -} - -impl LengthOrKeyword { - fn parse(context: &ParserContext, input: &mut Parser, parse_size_keyword: F) -> Result - where F: Fn(&mut Parser) -> Result - { - if let Ok(keyword) = input.try(parse_size_keyword) { - Ok(LengthOrKeyword::Keyword(keyword)) - } else { - Ok(LengthOrKeyword::Length(try!(Length::parse(context, input)))) - } - } -} - -impl ToCss for LengthOrKeyword { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LengthOrKeyword::Length(ref length) => length.to_css(dest), - LengthOrKeyword::Keyword(keyword) => keyword.to_css(dest), - } - } -} - -/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-size -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub enum LengthOrPercentageOrKeyword { - LengthOrPercentage(LengthOrPercentage, LengthOrPercentage), - Keyword(SizeKeyword), -} - - -impl LengthOrPercentageOrKeyword { - fn parse(context: &ParserContext, input: &mut Parser, parse_size_keyword: F) -> Result - where F: Fn(&mut Parser) -> Result - { - if let Ok(keyword) = input.try(parse_size_keyword) { - Ok(LengthOrPercentageOrKeyword::Keyword(keyword)) - } else { - Ok(LengthOrPercentageOrKeyword::LengthOrPercentage( - try!(LengthOrPercentage::parse(context, input)), - try!(LengthOrPercentage::parse(context, input)))) - } - } -} - -impl ToCss for LengthOrPercentageOrKeyword { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LengthOrPercentageOrKeyword::LengthOrPercentage(ref first_len, ref second_len) => { - try!(first_len.to_css(dest)); - try!(dest.write_str(" ")); - second_len.to_css(dest) - }, - LengthOrPercentageOrKeyword::Keyword(keyword) => keyword.to_css(dest), - } - } -} - -/// https://drafts.csswg.org/css-images/#typedef-extent-keyword -define_css_keyword_enum!(SizeKeyword: "closest-side" => ClosestSide, "farthest-side" => FarthestSide, - "closest-corner" => ClosestCorner, "farthest-corner" => FarthestCorner, - "contain" => Contain, "cover" => Cover); - -impl SizeKeyword { - fn parse_modern(input: &mut Parser) -> Result { - match try!(SizeKeyword::parse(input)) { - SizeKeyword::Contain | SizeKeyword::Cover => Err(()), - keyword => Ok(keyword), - } - } -} - -/// Specified values for none | | . -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct LayerImage(pub Option); -no_viewport_percentage!(LayerImage); - -impl ToCss for LayerImage { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LayerImage(Some(ref image)) => image.to_css(dest), - LayerImage(None) => dest.write_str("none"), - } - } -} - -use super::computed::{ToComputedValue, Context}; -impl ToComputedValue for LayerImage { - type ComputedValue = super::computed::LayerImage; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - match *self { - LayerImage(None) => super::computed::LayerImage(None), - LayerImage(Some(ref image)) => - super::computed::LayerImage(Some(image.to_computed_value(context))), - } - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - match *computed { - super::computed::LayerImage(None) => LayerImage(None), - super::computed::LayerImage(Some(ref image)) => - LayerImage(Some(ToComputedValue::from_computed_value(image))), - } - } -} - -impl Parse for LayerImage { +impl Parse for ImageRect { fn parse(context: &ParserContext, input: &mut Parser) -> Result { - if input.try(|input| input.expect_ident_matching("none")).is_ok() { - Ok(LayerImage(None)) - } else { - Ok(LayerImage(Some(try!(Image::parse(context, input))))) - } + input.try(|i| i.expect_function_matching("-moz-image-rect"))?; + input.parse_nested_block(|i| { + let string = i.expect_url_or_string()?; + let url = SpecifiedUrl::parse_from_string(string, context)?; + i.expect_comma()?; + let top = NumberOrPercentage::parse_non_negative(context, i)?; + i.expect_comma()?; + let right = NumberOrPercentage::parse_non_negative(context, i)?; + i.expect_comma()?; + let bottom = NumberOrPercentage::parse_non_negative(context, i)?; + i.expect_comma()?; + let left = NumberOrPercentage::parse_non_negative(context, i)?; + + Ok(ImageRect { + url: url, + top: top, + right: right, + bottom: bottom, + left: left, + }) + }) } } diff --git a/servo/components/style/values/specified/length.rs b/servo/components/style/values/specified/length.rs index 1918c12ac0e9..7a6f62597328 100644 --- a/servo/components/style/values/specified/length.rs +++ b/servo/components/style/values/specified/length.rs @@ -15,7 +15,7 @@ use std::{cmp, fmt, mem}; use std::ascii::AsciiExt; use std::ops::Mul; use style_traits::ToCss; -use style_traits::values::specified::AllowedLengthType; +use style_traits::values::specified::{AllowedLengthType, AllowedNumericType}; use stylesheets::CssRuleType; use super::{AllowQuirks, Number, ToComputedValue}; use values::{Auto, CSSFloat, Either, FONT_MEDIUM_PX, HasViewportPercentage, None_, Normal}; @@ -24,9 +24,8 @@ use values::computed::{ComputedValueAsSpecified, Context}; use values::specified::calc::CalcNode; pub use values::specified::calc::CalcLengthOrPercentage; -pub use super::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient}; -pub use super::image::{GradientKind, HorizontalDirection, Image, LengthOrKeyword, LengthOrPercentageOrKeyword}; -pub use super::image::{SizeKeyword, VerticalDirection}; +pub use super::image::{ColorStop, EndingShape as GradientEndingShape, Gradient}; +pub use super::image::{GradientKind, Image}; /// Number of app units per pixel pub const AU_PER_PX: CSSFloat = 60.; @@ -730,7 +729,10 @@ impl ToCss for Percentage { } impl Percentage { - fn parse_internal(input: &mut Parser, context: AllowedLengthType) -> Result { + /// Parse a specific kind of percentage. + pub fn parse_with_clamping_mode(input: &mut Parser, + context: AllowedNumericType) + -> Result { match try!(input.next()) { Token::Percentage(ref value) if context.is_ok(value.unit_value) => { Ok(Percentage(value.unit_value)) @@ -741,14 +743,14 @@ impl Percentage { /// Parses a percentage token, but rejects it if it's negative. pub fn parse_non_negative(input: &mut Parser) -> Result { - Self::parse_internal(input, AllowedLengthType::NonNegative) + Self::parse_with_clamping_mode(input, AllowedNumericType::NonNegative) } } impl Parse for Percentage { #[inline] fn parse(_context: &ParserContext, input: &mut Parser) -> Result { - Self::parse_internal(input, AllowedLengthType::All) + Self::parse_with_clamping_mode(input, AllowedNumericType::All) } } diff --git a/servo/components/style/values/specified/mod.rs b/servo/components/style/values/specified/mod.rs index 7dea44b085f2..04df41c542a2 100644 --- a/servo/components/style/values/specified/mod.rs +++ b/servo/components/style/values/specified/mod.rs @@ -28,9 +28,8 @@ use values::specified::calc::CalcNode; pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; pub use self::color::Color; pub use self::grid::{GridLine, TrackKeyword}; -pub use self::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient}; -pub use self::image::{GradientItem, GradientKind, HorizontalDirection, Image, ImageRect, LayerImage}; -pub use self::image::{LengthOrKeyword, LengthOrPercentageOrKeyword, SizeKeyword, VerticalDirection}; +pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient}; +pub use self::image::{GradientItem, GradientKind, Image, ImageRect, ImageLayer}; pub use self::length::AbsoluteLength; pub use self::length::{FontRelativeLength, ViewportPercentageLength, CharacterWidth, Length, CalcLengthOrPercentage}; pub use self::length::{Percentage, LengthOrNone, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; @@ -681,7 +680,7 @@ impl ToCss for Number { /// /// Accepts only non-negative numbers. -#[derive(Debug, Clone, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] pub enum NumberOrPercentage { @@ -691,13 +690,27 @@ pub enum NumberOrPercentage { no_viewport_percentage!(NumberOrPercentage); -impl Parse for NumberOrPercentage { - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - if let Ok(per) = input.try(Percentage::parse_non_negative) { +impl NumberOrPercentage { + fn parse_with_clamping_mode(context: &ParserContext, + input: &mut Parser, + type_: AllowedNumericType) + -> Result { + if let Ok(per) = input.try(|i| Percentage::parse_with_clamping_mode(i, type_)) { return Ok(NumberOrPercentage::Percentage(per)); } - Number::parse_non_negative(context, input).map(NumberOrPercentage::Number) + parse_number_with_clamping_mode(context, input, type_).map(NumberOrPercentage::Number) + } + + /// Parse a non-negative number or percentage. + pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result { + Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative) + } +} + +impl Parse for NumberOrPercentage { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + Self::parse_with_clamping_mode(context, input, AllowedNumericType::All) } } diff --git a/servo/ports/geckolib/glue.rs b/servo/ports/geckolib/glue.rs index eb70d86eb0e3..adb25ae19a04 100644 --- a/servo/ports/geckolib/glue.rs +++ b/servo/ports/geckolib/glue.rs @@ -1835,8 +1835,8 @@ pub extern "C" fn Servo_DeclarationBlock_SetBackgroundImage(declarations: raw_extra_data: *mut URLExtraData) { use style::properties::PropertyDeclaration; use style::properties::longhands::background_image::SpecifiedValue as BackgroundImage; - use style::properties::longhands::background_image::single_value::SpecifiedValue as SingleBackgroundImage; - use style::values::specified::image::Image; + use style::values::Either; + use style::values::generics::image::Image; use style::values::specified::url::SpecifiedUrl; let url_data = unsafe { RefPtr::from_ptr_ref(&raw_extra_data) }; @@ -1847,9 +1847,7 @@ pub extern "C" fn Servo_DeclarationBlock_SetBackgroundImage(declarations: QuirksMode::NoQuirks); if let Ok(url) = SpecifiedUrl::parse_from_string(string.into(), &context) { let decl = PropertyDeclaration::BackgroundImage(BackgroundImage( - vec![SingleBackgroundImage( - Some(Image::Url(url)) - )] + vec![Either::Second(Image::Url(url))] )); write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| { decls.push(decl, Importance::Normal); diff --git a/servo/tests/unit/style/parsing/image.rs b/servo/tests/unit/style/parsing/image.rs index 708c50b0d938..481fbbb952d3 100644 --- a/servo/tests/unit/style/parsing/image.rs +++ b/servo/tests/unit/style/parsing/image.rs @@ -2,16 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use euclid::size::TypedSize2D; use parsing::parse; -use std::f32::consts::PI; -use style::context::QuirksMode; -use style::font_metrics::ServoMetricsProvider; -use style::media_queries::{Device, MediaType}; -use style::properties::{ComputedValues, StyleBuilder}; -use style::values::computed; -use style::values::computed::{Angle, Context, ToComputedValue}; -use style::values::specified; +use style::parser::Parse; use style::values::specified::image::*; use style_traits::ToCss; @@ -37,101 +29,77 @@ fn test_linear_gradient() { // Parsing without and assert_roundtrip_with_context!(Image::parse, "linear-gradient(red, green)"); - - // AngleOrCorner::None should become AngleOrCorner::Angle(Angle(PI)) when parsed - // Note that Angle(PI) is correct for top-to-bottom rendering, whereas Angle(0) would render bottom-to-top. - // ref: https://developer.mozilla.org/en-US/docs/Web/CSS/angle - let viewport_size = TypedSize2D::new(0., 0.); - let initial_style = ComputedValues::initial_values(); - let device = Device::new(MediaType::Screen, viewport_size); - let specified_context = Context { - is_root_element: true, - device: &device, - inherited_style: initial_style, - layout_parent_style: initial_style, - style: StyleBuilder::for_derived_style(&initial_style), - cached_system_font: None, - font_metrics_provider: &ServoMetricsProvider, - in_media_query: false, - quirks_mode: QuirksMode::NoQuirks, - }; - assert_eq!(specified::AngleOrCorner::None.to_computed_value(&specified_context), - computed::AngleOrCorner::Angle(Angle::from_radians(PI))); } #[test] fn test_radial_gradient() { // Parsing with all values assert_roundtrip_with_context!(Image::parse, "radial-gradient(circle closest-side at 20px 30px, red, green)"); - assert_roundtrip_with_context!(Image::parse, "radial-gradient(ellipse closest-side at 20px 30px, red, green)"); + assert_roundtrip_with_context!(Image::parse, "radial-gradient(ellipse closest-side at 20px 30px, red, green)", + "radial-gradient(closest-side at 20px 30px, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side circle at 20px 30px, red, green)", "radial-gradient(circle closest-side at 20px 30px, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side ellipse at 20px 30px, red, green)", - "radial-gradient(ellipse closest-side at 20px 30px, red, green)"); + "radial-gradient(closest-side at 20px 30px, red, green)"); // Parsing with and reversed assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side circle at 20px 30px, red, green)", "radial-gradient(circle closest-side at 20px 30px, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-corner ellipse at 20px 30px, red, green)", - "radial-gradient(ellipse closest-corner at 20px 30px, red, green)"); + "radial-gradient(closest-corner at 20px 30px, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(30px circle, red, green)", - "radial-gradient(circle 30px at center center, red, green)"); + "radial-gradient(30px at center center, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(30px 40px ellipse, red, green)", - "radial-gradient(ellipse 30px 40px at center center, red, green)"); + "radial-gradient(30px 40px at center center, red, green)"); // Parsing without assert_roundtrip_with_context!(Image::parse, "radial-gradient(circle, red, green)", - "radial-gradient(circle farthest-corner at center center, red, green)"); + "radial-gradient(circle at center center, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(ellipse, red, green)", - "radial-gradient(ellipse farthest-corner at center center, red, green)"); + "radial-gradient(at center center, red, green)"); assert_roundtrip_with_context!(Image::parse, - "radial-gradient(circle at 20px 30px, red, green)", - "radial-gradient(circle farthest-corner at 20px 30px, red, green)"); + "radial-gradient(circle at 20px 30px, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(ellipse at 20px 30px, red, green)", - "radial-gradient(ellipse farthest-corner at 20px 30px, red, green)"); + "radial-gradient(at 20px 30px, red, green)"); // Parsing without assert_roundtrip_with_context!(Image::parse, - "radial-gradient(20px at 20px 30px, red, green)", - "radial-gradient(circle 20px at 20px 30px, red, green)"); + "radial-gradient(20px at 20px 30px, red, green)"); assert_roundtrip_with_context!(Image::parse, - "radial-gradient(20px 30px at left center, red, green)", - "radial-gradient(ellipse 20px 30px at left center, red, green)"); + "radial-gradient(20px 30px at left center, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side at center, red, green)", - "radial-gradient(ellipse closest-side at center center, red, green)"); + "radial-gradient(closest-side at center center, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(20px, red, green)", - "radial-gradient(circle 20px at center center, red, green)"); + "radial-gradient(20px at center center, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(20px 30px, red, green)", - "radial-gradient(ellipse 20px 30px at center center, red, green)"); + "radial-gradient(20px 30px at center center, red, green)"); assert_roundtrip_with_context!(Image::parse, "radial-gradient(closest-side, red, green)", - "radial-gradient(ellipse closest-side at center center, red, green)"); + "radial-gradient(closest-side at center center, red, green)"); // Parsing without and assert_roundtrip_with_context!(Image::parse, "radial-gradient(at center, red, green)", - "radial-gradient(ellipse farthest-corner at center center, red, green)"); + "radial-gradient(at center center, red, green)"); assert_roundtrip_with_context!(Image::parse, - "radial-gradient(at center bottom, red, green)", - "radial-gradient(ellipse farthest-corner at center bottom, red, green)"); + "radial-gradient(at center bottom, red, green)"); assert_roundtrip_with_context!(Image::parse, - "radial-gradient(at 40px 50px, red, green)", - "radial-gradient(ellipse farthest-corner at 40px 50px, red, green)"); + "radial-gradient(at 40px 50px, red, green)"); // Parsing with just color stops assert_roundtrip_with_context!(Image::parse, "radial-gradient(red, green)", - "radial-gradient(ellipse farthest-corner at center center, red, green)"); + "radial-gradient(at center center, red, green)"); // Parsing repeating radial gradient assert_roundtrip_with_context!(Image::parse, "repeating-radial-gradient(red, green)", - "repeating-radial-gradient(ellipse farthest-corner at center center, red, green)"); + "repeating-radial-gradient(at center center, red, green)"); } diff --git a/servo/tests/unit/style/properties/serialization.rs b/servo/tests/unit/style/properties/serialization.rs index 426597e56b48..385aa9002147 100644 --- a/servo/tests/unit/style/properties/serialization.rs +++ b/servo/tests/unit/style/properties/serialization.rs @@ -799,7 +799,8 @@ mod shorthand_serialization { use style::properties::longhands::mask_position_y as position_y; use style::properties::longhands::mask_repeat as repeat; use style::properties::longhands::mask_size as size; - use style::values::specified::Image; + use style::values::Either; + use style::values::generics::image::Image; use super::*; macro_rules! single_vec_value_typedef { @@ -828,9 +829,10 @@ mod shorthand_serialization { fn mask_should_serialize_all_available_properties_when_specified() { let mut properties = Vec::new(); - let image = single_vec_value_typedef!(image, - image::single_value::SpecifiedValue( - Some(Image::Url(SpecifiedUrl::new_for_testing("http://servo/test.png"))))); + let image = single_vec_value_typedef!( + image, + Either::Second(Image::Url(SpecifiedUrl::new_for_testing("http://servo/test.png"))) + ); let mode = single_vec_keyword_value!(mode, luminance); @@ -880,9 +882,10 @@ mod shorthand_serialization { fn mask_should_combine_origin_and_clip_properties_when_equal() { let mut properties = Vec::new(); - let image = single_vec_value_typedef!(image, - image::single_value::SpecifiedValue( - Some(Image::Url(SpecifiedUrl::new_for_testing("http://servo/test.png"))))); + let image = single_vec_value_typedef!( + image, + Either::Second(Image::Url(SpecifiedUrl::new_for_testing("http://servo/test.png"))) + ); let mode = single_vec_keyword_value!(mode, luminance); From 215e98ef37331043556e2c726f23e3894d61e6bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Mon, 15 May 2017 17:22:48 +0200 Subject: [PATCH 24/30] Bug 1341102: Update test expectations for servo/servo#16859. r=emilio Patch by Anthony Ramine. MozReview-Commit-ID: 7uTzBU8yG0E --- layout/reftests/bugs/reftest.list | 4 ++-- layout/reftests/css-gradients/reftest.list | 2 +- .../forms/meter/default-style/reftest.list | 2 +- layout/reftests/forms/meter/reftest.list | 2 +- layout/reftests/image-rect/reftest.list | 6 +++--- layout/reftests/webkit-gradient/reftest.list | 16 ++++++++-------- layout/style/test/stylo-failures.md | 6 +++--- widget/reftests/reftest.list | 4 ++-- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 431a91cb99cd..e83b40efd63a 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1981,8 +1981,8 @@ fuzzy(8,1900) == 1291528.html 1291528-ref.html fuzzy(255,1000) skip-if(!cocoaWidget) == 1294102-1.html 1294102-1-ref.html random-if(Android) fuzzy-if(skiaContent,15,50) == 1295466-1.xhtml 1295466-1-ref.xhtml #bug 982547 fuzzy-if(Android,27,874) fuzzy-if(gtkWidget,14,29) == 1313772.xhtml 1313772-ref.xhtml # Bug 1128229 -fuzzy(2,320000) fails-if(stylo) == 1315113-1.html 1315113-1-ref.html -fuzzy(2,20000) fails-if(stylo) == 1315113-2.html 1315113-2-ref.html +fuzzy(2,320000) == 1315113-1.html 1315113-1-ref.html +fuzzy(2,20000) == 1315113-2.html 1315113-2-ref.html == 1315632-1.html 1315632-1-ref.html fuzzy(2,40000) == 1316719-1a.html 1316719-1-ref.html fuzzy(2,40000) == 1316719-1b.html 1316719-1-ref.html diff --git a/layout/reftests/css-gradients/reftest.list b/layout/reftests/css-gradients/reftest.list index 37d61d0763a9..6ba01dbf9da0 100644 --- a/layout/reftests/css-gradients/reftest.list +++ b/layout/reftests/css-gradients/reftest.list @@ -16,7 +16,7 @@ fails-if(stylo) == linear-diagonal-3c.html linear-diagonal-3-ref.html fails-if(stylo) == linear-diagonal-4a.html linear-diagonal-4-ref.html fails-if(stylo) == linear-diagonal-4b.html linear-diagonal-4-ref.html fails-if(stylo) == linear-diagonal-4c.html linear-diagonal-4-ref.html -fails-if(stylo) == linear-premul.html linear-premul-ref.html +== linear-premul.html linear-premul-ref.html # these tests uses a similar gradient over different bounds. It's perfectly # reasonable to expect implementations to give slightly different results diff --git a/layout/reftests/forms/meter/default-style/reftest.list b/layout/reftests/forms/meter/default-style/reftest.list index 98b977ca71f0..a67d6712b696 100644 --- a/layout/reftests/forms/meter/default-style/reftest.list +++ b/layout/reftests/forms/meter/default-style/reftest.list @@ -1,2 +1,2 @@ -fails-if(stylo) == default-style.html default-style-ref.html +== default-style.html default-style-ref.html == default-style-dyn.html default-style-dyn-ref.html diff --git a/layout/reftests/forms/meter/reftest.list b/layout/reftests/forms/meter/reftest.list index e412790e9ffc..0a92a6004251 100644 --- a/layout/reftests/forms/meter/reftest.list +++ b/layout/reftests/forms/meter/reftest.list @@ -24,7 +24,7 @@ include default-style/reftest.list # Tests for bugs: == block-invalidate.html block-invalidate-ref.html == in-cells.html in-cells-ref.html -fails-if(stylo) == max-height.html max-height-ref.html +== max-height.html max-height-ref.html # Tests for block and inline orientation in combination with writing-mode != meter-orient-vertical.html meter-orient-horizontal.html diff --git a/layout/reftests/image-rect/reftest.list b/layout/reftests/image-rect/reftest.list index 5c51f95c30b4..8992d0aab9da 100644 --- a/layout/reftests/image-rect/reftest.list +++ b/layout/reftests/image-rect/reftest.list @@ -6,11 +6,11 @@ == background-draw-nothing-malformed-images.html background-draw-nothing-ref.html == background-monster-rect.html background-monster-rect-ref.html == background-over-size-rect.html background-over-size-rect-ref.html -fails-if(stylo) == background-test-parser.html background-test-parser-ref.html +== background-test-parser.html background-test-parser-ref.html fuzzy-if(Android,113,124) == background-with-other-properties.html background-with-other-properties-ref.html fuzzy-if(Android,16,22) == background-zoom-1.html background-zoom-1-ref.html # Bug 1128229 == background-zoom-2.html background-zoom-2-ref.html == background-zoom-3.html background-zoom-3-ref.html == background-zoom-4.html background-zoom-4-ref.html -fails-if(stylo) == dom-api-computed-style.html dom-api-ref.html -fails-if(stylo) == dom-api.html dom-api-ref.html +== dom-api-computed-style.html dom-api-ref.html +== dom-api.html dom-api-ref.html diff --git a/layout/reftests/webkit-gradient/reftest.list b/layout/reftests/webkit-gradient/reftest.list index 611dfce3feba..7d8aef34c735 100644 --- a/layout/reftests/webkit-gradient/reftest.list +++ b/layout/reftests/webkit-gradient/reftest.list @@ -5,16 +5,16 @@ default-preferences pref(layout.css.prefixes.webkit,true) # In this test, we don't render a "-webkit-gradient" exactly correctly. # (It's just here to ensure that our approximate/do-something rendering doesn't # change unexpectedly.) -fails-if(stylo) == webkit-gradient-approx-radial-1.html webkit-gradient-approx-radial-1-ref.html +== webkit-gradient-approx-radial-1.html webkit-gradient-approx-radial-1-ref.html # Tests for -webkit-gradient(linear, ...) -fails-if(stylo) == webkit-gradient-linear-1a.html webkit-gradient-linear-1-ref.html -fails-if(stylo) == webkit-gradient-linear-1b.html webkit-gradient-linear-1-ref.html -fails-if(stylo) == webkit-gradient-linear-1c.html webkit-gradient-linear-1-ref.html -fails-if(stylo) == webkit-gradient-linear-1d.html webkit-gradient-linear-1-ref.html +== webkit-gradient-linear-1a.html webkit-gradient-linear-1-ref.html +== webkit-gradient-linear-1b.html webkit-gradient-linear-1-ref.html +== webkit-gradient-linear-1c.html webkit-gradient-linear-1-ref.html +== webkit-gradient-linear-1d.html webkit-gradient-linear-1-ref.html fails-if(stylo) == webkit-gradient-linear-2.html webkit-gradient-linear-2-ref.html # Tests for -webkit-gradient(radial, ...) -fails-if(stylo) == webkit-gradient-radial-1a.html webkit-gradient-radial-1-ref.html -fails-if(stylo) == webkit-gradient-radial-1b.html webkit-gradient-radial-1-ref.html -fails-if(stylo) == webkit-gradient-radial-2.html webkit-gradient-radial-2-ref.html +== webkit-gradient-radial-1a.html webkit-gradient-radial-1-ref.html +== webkit-gradient-radial-1b.html webkit-gradient-radial-1-ref.html +== webkit-gradient-radial-2.html webkit-gradient-radial-2-ref.html diff --git a/layout/style/test/stylo-failures.md b/layout/style/test/stylo-failures.md index f71a51d211b7..d78d77102f04 100644 --- a/layout/style/test/stylo-failures.md +++ b/layout/style/test/stylo-failures.md @@ -70,7 +70,7 @@ to mochitest command. * test_transitions.html: pseudo elements [12] * Events: * test_animations_event_order.html [2] -* test_computed_style.html `gradient`: -moz- and -webkit-prefixed gradient values [35] +* test_computed_style.html `gradient`: -moz- and -webkit-prefixed gradient values [22] * test_bug829816.html: counter-{reset,increment} serialization difference bug 1363968 [8] * \@counter-style support bug 1328319 * test_counter_descriptor_storage.html [1] @@ -121,8 +121,6 @@ to mochitest command. * ... `-moz-radial-gradient` [309] * ... `-moz-repeating-` [298] * test_specified_value_serialization.html `-moz-linear-gradient` [2] - * -webkit-gradient() bug 1363986 - * test_value_storage.html `-webkit-gradient` [225] * serialization of prefixed gradient functions bug 1358710 * test_specified_value_serialization.html `-webkit-linear-gradient` [1] * test_specified_value_serialization.html `-webkit-radial-gradient` [1] @@ -222,6 +220,8 @@ to mochitest command. * Gecko clamps rather than rejects invalid unicode range bug 1355356 * test_font_face_parser.html `U+??????` [2] * ... `12FFFF` [2] + * Gecko rejects calc() in -webkit-gradient bug 1363349 + * test_property_syntax_errors.html `-webkit-gradient` [20] * test_property_syntax_errors.html `linear-gradient(0,`: unitless zero as degree [10] ## Spec Unclear diff --git a/widget/reftests/reftest.list b/widget/reftests/reftest.list index 6bb456978950..9f954064ee5e 100644 --- a/widget/reftests/reftest.list +++ b/widget/reftests/reftest.list @@ -1,6 +1,6 @@ pref(layout.css.moz-appearance.enabled,true) pref(layout.css.appearance.enabled,true) skip-if(!cocoaWidget) != 507947.html about:blank pref(layout.css.moz-appearance.enabled,true) == progressbar-fallback-default-style.html progressbar-fallback-default-style-ref.html fuzzy-if(Android,17,1120) == meter-native-style.html meter-native-style-ref.html -skip-if(!cocoaWidget) fails-if(stylo) == meter-vertical-native-style.html meter-vertical-native-style-ref.html # dithering -pref(layout.css.moz-appearance.enabled,true) fails-if(stylo) == meter-fallback-default-style.html meter-fallback-default-style-ref.html +skip-if(!cocoaWidget) == meter-vertical-native-style.html meter-vertical-native-style-ref.html # dithering +pref(layout.css.moz-appearance.enabled,true) == meter-fallback-default-style.html meter-fallback-default-style-ref.html pref(layout.css.moz-appearance.enabled,true) pref(layout.css.appearance.enabled,true) load 664925.xhtml From 1d6a7d6e22df8eefa57bb3ba8bc87cd7a7c3475f Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Mon, 15 May 2017 10:14:38 +0000 Subject: [PATCH 25/30] Bug 1364851 - Explicitly include SkRegion.h for --disable-skia-gpu. r=lsalzman MozReview-Commit-ID: EEGNcLi538V --HG-- extra : rebase_source : 7d0c6c1486f206249fa5997fbdf0a7ba735f5c46 --- gfx/2d/DrawTargetSkia.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp index 8d4ae72dcdb1..77a817bc9d0b 100644 --- a/gfx/2d/DrawTargetSkia.cpp +++ b/gfx/2d/DrawTargetSkia.cpp @@ -16,6 +16,7 @@ #include "skia/include/core/SkTypeface.h" #include "skia/include/effects/SkGradientShader.h" #include "skia/include/core/SkColorFilter.h" +#include "skia/include/core/SkRegion.h" #include "skia/include/effects/SkBlurImageFilter.h" #include "skia/include/effects/SkLayerRasterizer.h" #include "skia/src/core/SkDevice.h" From 00a6026fe96bc347aac333ebf9bbde694b9e4956 Mon Sep 17 00:00:00 2001 From: Rajesh Kathiriya Date: Thu, 11 May 2017 23:54:25 +0530 Subject: [PATCH 26/30] bug 1359019 - Added Eslint file for the eslint-plugin-mozilla configs r=standard8 MozReview-Commit-ID: 9g9QC6EdJ2a --HG-- extra : rebase_source : af29a5c7daf3b358cf543654e0e2aa2ebc79563f --- .../lib/configs/.eslintrc.js | 8 + .../lib/configs/browser-test.js | 42 ++--- .../lib/configs/chrome-test.js | 28 +-- .../lib/configs/mochitest-test.js | 30 ++-- .../lib/configs/recommended.js | 166 +++++++++--------- .../lib/configs/xpcshell-test.js | 18 +- 6 files changed, 153 insertions(+), 139 deletions(-) create mode 100644 tools/lint/eslint/eslint-plugin-mozilla/lib/configs/.eslintrc.js diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/.eslintrc.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/.eslintrc.js new file mode 100644 index 000000000000..37de7296ba7a --- /dev/null +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "rules": { + // Require object keys to be sorted. + "sort-keys": "error" + } +}; \ No newline at end of file diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/browser-test.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/browser-test.js index c20d585a1041..391746d01c07 100644 --- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/browser-test.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/browser-test.js @@ -2,12 +2,6 @@ "use strict"; module.exports = { - "rules": { - "mozilla/import-content-task-globals": "error", - "mozilla/import-headjs-globals": "warn", - "mozilla/mark-test-function-used": "warn" - }, - "env": { "browser": true, "mozilla/browser-window": true, @@ -15,52 +9,58 @@ module.exports = { // "node": true }, - "plugins": [ - "mozilla" - ], - // All globals made available in the test environment. "globals": { // `$` is defined in SimpleTest.js "$": false, - "add_task": false, - "addLoadEvent": false, "Assert": false, "BrowserTestUtils": false, - "content": false, "ContentTask": false, "ContentTaskUtils": false, "EventUtils": false, + "PromiseDebugging": false, + "SpecialPowers": false, + "TestUtils": false, + "XPCNativeWrapper": false, + "XULDocument": false, + "addLoadEvent": false, + "add_task": false, + "content": false, "executeSoon": false, "expectUncaughtException": false, "export_assertions": false, "extractJarToTmp": false, "finish": false, + "gTestPath": false, "getChromeDir": false, "getJar": false, "getResolvedURI": false, "getRootDirectory": false, "getTestFilePath": false, - "gTestPath": false, - "info": false, "ignoreAllUncaughtExceptions": false, + "info": false, "is": false, "isnot": false, "ok": false, - "PromiseDebugging": false, "privateNoteIntentionalCrash": false, "registerCleanupFunction": false, "requestLongerTimeout": false, - "SpecialPowers": false, - "TestUtils": false, "thisTestLeaksUncaughtRejectionsAndShouldBeFixed": false, "todo": false, "todo_is": false, "todo_isnot": false, "waitForClipboard": false, "waitForExplicitFinish": false, - "waitForFocus": false, - "XPCNativeWrapper": false, - "XULDocument": false + "waitForFocus": false + }, + + "plugins": [ + "mozilla" + ], + + "rules": { + "mozilla/import-content-task-globals": "error", + "mozilla/import-headjs-globals": "warn", + "mozilla/mark-test-function-used": "warn" } }; diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/chrome-test.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/chrome-test.js index 2e04845ddc1d..54fb6271abff 100644 --- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/chrome-test.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/chrome-test.js @@ -2,40 +2,31 @@ "use strict"; module.exports = { - rules: { - "mozilla/import-content-task-globals": "error", - "mozilla/import-headjs-globals": "warn", - "mozilla/mark-test-function-used": "warn" - }, - "env": { "browser": true, "mozilla/browser-window": true, "mozilla/simpletest": true }, - "plugins": [ - "mozilla" - ], - // All globals made available in the test environment. "globals": { // `$` is defined in SimpleTest.js "$": false, - "add_task": false, - "addLoadEvent": false, "Assert": false, "BrowserTestUtils": false, "ContentTask": false, "EventUtils": false, + "SpecialPowers": false, + "addLoadEvent": false, + "add_task": false, "executeSoon": false, "export_assertions": false, "extractJarToTmp": false, "finish": false, + "gTestPath": false, "getJar": false, "getRootDirectory": false, "getTestFilePath": false, - "gTestPath": false, "info": false, "is": false, "isnot": false, @@ -44,12 +35,21 @@ module.exports = { "promise": false, "registerCleanupFunction": false, "requestLongerTimeout": false, - "SpecialPowers": false, "todo": false, "todo_is": false, "todo_isnot": false, "waitForClipboard": false, "waitForExplicitFinish": false, "waitForFocus": false + }, + + "plugins": [ + "mozilla" + ], + + rules: { + "mozilla/import-content-task-globals": "error", + "mozilla/import-headjs-globals": "warn", + "mozilla/mark-test-function-used": "warn" } }; diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/mochitest-test.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/mochitest-test.js index 46eeaf74782f..47e0628d9a51 100644 --- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/mochitest-test.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/mochitest-test.js @@ -2,36 +2,26 @@ "use strict"; module.exports = { - "rules": { - "mozilla/import-content-task-globals": "error", - "mozilla/import-headjs-globals": "warn", - "mozilla/mark-test-function-used": "warn", - "no-shadow": "error" - }, - "env": { "browser": true, "mozilla/simpletest": true }, - "plugins": [ - "mozilla" - ], - // All globals made available in the test environment. "globals": { // `$` is defined in SimpleTest.js "$": false, - "add_task": false, - "addLoadEvent": false, "Assert": false, "EventUtils": false, + "SpecialPowers": false, + "addLoadEvent": false, + "add_task": false, "executeSoon": false, "export_assertions": false, "finish": false, + "gTestPath": false, "getRootDirectory": false, "getTestFilePath": false, - "gTestPath": false, "info": false, "is": false, "isDeeply": false, @@ -41,12 +31,22 @@ module.exports = { "promise": false, "registerCleanupFunction": false, "requestLongerTimeout": false, - "SpecialPowers": false, "todo": false, "todo_is": false, "todo_isnot": false, "waitForClipboard": false, "waitForExplicitFinish": false, "waitForFocus": false + }, + + "plugins": [ + "mozilla" + ], + + "rules": { + "mozilla/import-content-task-globals": "error", + "mozilla/import-headjs-globals": "warn", + "mozilla/mark-test-function-used": "warn", + "no-shadow": "error" } }; diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js index aacd702dff62..54216a138d3c 100644 --- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js @@ -1,36 +1,64 @@ "use strict"; module.exports = { - // When adding items to this file please check for effects on sub-directories. - "plugins": [ - "mozilla" - ], "env": { "browser": true, "es6": true }, + + "globals": { + "BroadcastChannel": false, + "CSSPrimitiveValue": false, + "CSSValueList": false, + // Specific to Firefox (Chrome code only). + "ChromeUtils": false, + "ChromeWindow": false, + "ChromeWorker": false, + "Components": false, + "ImageDocument": false, + "InstallTrigger": false, + // Specific to Firefox + // eslint-disable-next-line max-len + // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/InternalError + "InternalError": true, + "KeyEvent": false, + "MenuBoxObject": false, + // Specific to Firefox (Chrome code only). + "MozSelfSupport": false, + "SharedArrayBuffer": false, + "SimpleGestureEvent": false, + // Note: StopIteration will likely be removed as part of removing legacy + // generators, see bug 968038. + "StopIteration": false, + // Non-standard, specific to Firefox. + "XULElement": false, + "dump": true, + "openDialog": false, + "sizeToContent": false, + // Specific to Firefox + // eslint-disable-next-line max-len + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/uneval + "uneval": false + }, + "parserOptions": { "ecmaVersion": 8 }, + + // When adding items to this file please check for effects on sub-directories. + "plugins": [ + "mozilla" + ], + // When adding items to this file please check for effects on all of toolkit // and browser "rules": { - "mozilla/avoid-removeChild": "error", - "mozilla/avoid-nsISupportsString-preferences": "error", - "mozilla/import-browser-window-globals": "error", - "mozilla/import-globals": "error", - "mozilla/no-import-into-var-and-global": "error", - "mozilla/no-useless-parameters": "error", - "mozilla/no-useless-removeEventListener": "error", - "mozilla/use-default-preference-values": "error", - "mozilla/use-ownerGlobal": "error", - - // Braces only needed for multi-line arrow function blocks - // "arrow-body-style": ["error", "as-needed"], - // Require spacing around => "arrow-spacing": "error", + // Braces only needed for multi-line arrow function blocks + // "arrow-body-style": ["error", "as-needed"] + // Always require spacing around a single line block "block-spacing": "error", @@ -38,7 +66,7 @@ module.exports = { "brace-style": ["error", "1tbs", { "allowSingleLine": true }], // No space before always a space after a comma - "comma-spacing": ["error", {"before": false, "after": true}], + "comma-spacing": ["error", {"after": true, "before": false}], // Commas at the end of the line not the start // "comma-style": "error", @@ -72,8 +100,8 @@ module.exports = { // Space after colon not before in property declarations "key-spacing": ["error", { - "beforeColon": false, "afterColon": true, + "beforeColon": false, "mode": "minimum" }], @@ -90,6 +118,16 @@ module.exports = { // Maximum depth callbacks can be nested. "max-nested-callbacks": ["error", 10], + "mozilla/avoid-nsISupportsString-preferences": "error", + "mozilla/avoid-removeChild": "error", + "mozilla/import-browser-window-globals": "error", + "mozilla/import-globals": "error", + "mozilla/no-import-into-var-and-global": "error", + "mozilla/no-useless-parameters": "error", + "mozilla/no-useless-removeEventListener": "error", + "mozilla/use-default-preference-values": "error", + "mozilla/use-ownerGlobal": "error", + // Always require parenthesis for new calls // "new-parens": "error", @@ -114,19 +152,6 @@ module.exports = { // No duplicate cases in switch statements "no-duplicate-case": "error", - // Disallow unnecessary calls to .bind() - "no-extra-bind": "error", - - // Disallow eval and setInteral/setTimeout with strings - "no-implied-eval": "error", - "no-eval": "error", - - // No labels - "no-labels": "error", - - // Disallow unnecessary nested blocks - "no-lone-blocks": "error", - // If an if block ends with a return no need for an else block "no-else-return": "error", @@ -139,9 +164,15 @@ module.exports = { // Disallow empty destructuring "no-empty-pattern": "error", + // Disallow eval and setInteral/setTimeout with strings + "no-eval": "error", + // No assigning to exception variable "no-ex-assign": "error", + // Disallow unnecessary calls to .bind() + "no-extra-bind": "error", + // No using !! where casting to boolean is already happening "no-extra-boolean-cast": "error", @@ -151,6 +182,9 @@ module.exports = { // No overwriting defined functions "no-func-assign": "error", + // Disallow eval and setInteral/setTimeout with strings + "no-implied-eval": "error", + // No invalid regular expresions "no-invalid-regexp": "error", @@ -160,6 +194,12 @@ module.exports = { // Disallow the use of the __iterator__ property "no-iterator": "error", + // No labels + "no-labels": "error", + + // Disallow unnecessary nested blocks + "no-lone-blocks": "error", + // No single if block inside an else block "no-lonely-if": "error", @@ -168,10 +208,10 @@ module.exports = { // No unnecessary spacing "no-multi-spaces": ["error", { exceptions: { - "AssignmentExpression": true, - "VariableDeclarator": true, "ArrayExpression": true, - "ObjectExpression": true + "AssignmentExpression": true, + "ObjectExpression": true, + "VariableDeclarator": true } }], // No reassigning native JS objects @@ -230,9 +270,9 @@ module.exports = { // No declaring variables that are never used "no-unused-vars": ["error", { + "args": "none", "vars": "local", - "varsIgnorePattern": "^Cc|Ci|Cu|Cr|EXPORTED_SYMBOLS", - "args": "none" + "varsIgnorePattern": "^Cc|Ci|Cu|Cr|EXPORTED_SYMBOLS" }], // No using variables before defined @@ -241,6 +281,10 @@ module.exports = { // Disallow unnecessary .call() and .apply() "no-useless-call": "error", + // Don't concatenate string literals together (unless they span multiple + // lines) + "no-useless-concat": "error", + // Disallow redundant return statements "no-useless-return": "error", @@ -253,8 +297,8 @@ module.exports = { // Require double-quotes everywhere, except where quotes are escaped // or template literals are used. "quotes": ["error", "double", { - "avoidEscape": true, - "allowTemplateLiterals": true + "allowTemplateLiterals": true, + "avoidEscape": true }], // No spacing inside rest or spread expressions @@ -277,11 +321,11 @@ module.exports = { // ++ and -- should not need spacing "space-unary-ops": ["error", { - "words": true, "nonwords": false, "overrides": { "typeof": false // We tend to use typeof as a function call - } + }, + "words": true }], // Requires or disallows a whitespace (space or tab) beginning a comment @@ -291,44 +335,6 @@ module.exports = { "use-isnan": "error", // Only check typeof against valid results - "valid-typeof": "error", - - // Don't concatenate string literals together (unless they span multiple - // lines) - "no-useless-concat": "error" - }, - "globals": { - "BroadcastChannel": false, - // Specific to Firefox (Chrome code only). - "ChromeWindow": false, - "ChromeWorker": false, - "ChromeUtils": false, - "Components": false, - "CSSPrimitiveValue": false, - "CSSValueList": false, - "dump": true, - "ImageDocument": false, - // Non-standard, specific to Firefox. - "InstallTrigger": false, - // Specific to Firefox - // eslint-disable-next-line max-len - // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/InternalError - "InternalError": true, - "KeyEvent": false, - "openDialog": false, - "MenuBoxObject": false, - // Specific to Firefox (Chrome code only). - "MozSelfSupport": false, - "SimpleGestureEvent": false, - "sizeToContent": false, - "SharedArrayBuffer": false, - // Note: StopIteration will likely be removed as part of removing legacy - // generators, see bug 968038. - "StopIteration": false, - // Specific to Firefox - // eslint-disable-next-line max-len - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/uneval - "uneval": false, - "XULElement": false + "valid-typeof": "error" } }; diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/xpcshell-test.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/xpcshell-test.js index e98ab193cfb9..08256c08b42c 100644 --- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/xpcshell-test.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/xpcshell-test.js @@ -2,18 +2,13 @@ "use strict"; module.exports = { - rules: { - "mozilla/import-headjs-globals": "warn", - "mozilla/mark-test-function-used": "warn", - "no-shadow": "error" - }, - // All globals made available in the test environment. "globals": { + "Assert": false, + "PromiseDebugging": false, "_TEST_FILE": false, "add_task": false, "add_test": false, - "Assert": false, "deepEqual": false, "do_await_remote_message": false, "do_check_eq": false, @@ -54,11 +49,10 @@ module.exports = { "notEqual": false, "notStrictEqual": false, "ok": false, - "PromiseDebugging": false, - "runningInParent": false, "run_next_test": false, "run_test": false, "run_test_in_child": false, + "runningInParent": false, // Defined in XPCShellImpl. "sendCommand": false, "strictEqual": false, @@ -70,5 +64,11 @@ module.exports = { // eslint-disable-next-line max-len // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/uneval "uneval": false + }, + + rules: { + "mozilla/import-headjs-globals": "warn", + "mozilla/mark-test-function-used": "warn", + "no-shadow": "error" } }; From 3d2e65ee7ec8767a5c13d45510b9c59fb9b62735 Mon Sep 17 00:00:00 2001 From: Edouard Oger Date: Fri, 12 May 2017 17:01:25 -0400 Subject: [PATCH 27/30] Bug 1364571 - Delay Weave startup. r=kitcambridge MozReview-Commit-ID: 4bm1LXLD7lV --HG-- extra : rebase_source : c1b14b70e4d952870800387f16c7fecab9667b49 --- browser/components/nsBrowserGlue.js | 7 +++ services/sync/SyncComponents.manifest | 12 ------ services/sync/Weave.js | 62 +++++++++++---------------- 3 files changed, 33 insertions(+), 48 deletions(-) diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 76c3cdd17391..0a3cf4463132 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -16,6 +16,9 @@ Cu.import("resource://gre/modules/AsyncPrefs.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils", "@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils"); XPCOMUtils.defineLazyServiceGetter(this, "AlertsService", "@mozilla.org/alerts-service;1", "nsIAlertsService"); +XPCOMUtils.defineLazyGetter(this, "WeaveService", () => + Cc["@mozilla.org/weave/service;1"].getService().wrappedJSObject +); // lazy module getters @@ -973,6 +976,10 @@ BrowserGlue.prototype = { AutoCompletePopup.init(); DateTimePickerHelper.init(); + // Check if Sync is configured + if (Services.prefs.prefHasUserValue("services.sync.username")) { + WeaveService.init(); + } this._firstWindowTelemetry(aWindow); this._firstWindowLoaded(); diff --git a/services/sync/SyncComponents.manifest b/services/sync/SyncComponents.manifest index 6493bb2242ef..faeafd8b6d4b 100644 --- a/services/sync/SyncComponents.manifest +++ b/services/sync/SyncComponents.manifest @@ -1,18 +1,6 @@ -# WeaveService has to restrict its registration for the app-startup category -# to the specific list of apps that use it so it doesn't get loaded in xpcshell. -# Thus we restrict it to these apps: -# -# b2g: {3c2e2abc-06d4-11e1-ac3b-374f68613e61} -# browser: {ec8030f7-c20a-464f-9b0e-13a3a9e97384} -# mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110} -# mobile/xul: {a23983c0-fd0e-11dc-95ff-0800200c9a66} -# suite (comm): {92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} -# graphene: {d1bfe7d9-c01e-4237-998b-7b5f960a4314} - # Weave.js component {74b89fb0-f200-4ae8-a3ec-dd164117f6de} Weave.js contract @mozilla.org/weave/service;1 {74b89fb0-f200-4ae8-a3ec-dd164117f6de} -category app-startup WeaveService service,@mozilla.org/weave/service;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} application={92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} application={99bceaaa-e3c6-48c1-b981-ef9b46b67d60} application={d1bfe7d9-c01e-4237-998b-7b5f960a4314} component {d28f8a0b-95da-48f4-b712-caf37097be41} Weave.js contract @mozilla.org/network/protocol/about;1?what=sync-log {d28f8a0b-95da-48f4-b712-caf37097be41} diff --git a/services/sync/Weave.js b/services/sync/Weave.js index 2375ccc587a9..966e94888124 100644 --- a/services/sync/Weave.js +++ b/services/sync/Weave.js @@ -94,6 +94,32 @@ WeaveService.prototype = { return onReadyPromise; }, + init() { + // Force Weave service to load if it hasn't triggered from overlays + this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + this.timer.initWithCallback({ + notify: () => { + let isConfigured = false; + // We only load more if it looks like Sync is configured. + if (this.enabled) { + // We have an associated FxAccount. So, do a more thorough check. + // This will import a number of modules and thus increase memory + // accordingly. We could potentially copy code performed by + // this check into this file if our above code is yielding too + // many false positives. + Components.utils.import("resource://services-sync/main.js"); + isConfigured = Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED; + } + let getHistogramById = Services.telemetry.getHistogramById; + getHistogramById("WEAVE_CONFIGURED").add(isConfigured); + if (isConfigured) { + getHistogramById("WEAVE_CONFIGURED_MASTER_PASSWORD").add(Utils.mpEnabled()); + this.ensureLoaded(); + } + } + }, 10000, Ci.nsITimer.TYPE_ONE_SHOT); + }, + /** * Whether Sync appears to be enabled. * @@ -105,42 +131,6 @@ WeaveService.prototype = { get enabled() { let prefs = Services.prefs.getBranch(SYNC_PREFS_BRANCH); return prefs.prefHasUserValue("username"); - }, - - observe(subject, topic, data) { - switch (topic) { - case "app-startup": - let os = Cc["@mozilla.org/observer-service;1"]. - getService(Ci.nsIObserverService); - os.addObserver(this, "final-ui-startup", true); - break; - - case "final-ui-startup": - // Force Weave service to load if it hasn't triggered from overlays - this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this.timer.initWithCallback({ - notify: () => { - let isConfigured = false; - // We only load more if it looks like Sync is configured. - if (this.enabled) { - // We have an associated FxAccount. So, do a more thorough check. - // This will import a number of modules and thus increase memory - // accordingly. We could potentially copy code performed by - // this check into this file if our above code is yielding too - // many false positives. - Components.utils.import("resource://services-sync/main.js"); - isConfigured = Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED; - } - let getHistogramById = Services.telemetry.getHistogramById; - getHistogramById("WEAVE_CONFIGURED").add(isConfigured); - if (isConfigured) { - getHistogramById("WEAVE_CONFIGURED_MASTER_PASSWORD").add(Utils.mpEnabled()); - this.ensureLoaded(); - } - } - }, 10000, Ci.nsITimer.TYPE_ONE_SHOT); - break; - } } }; From 45fdbb57305152a6365ba6d66de559166b7598c9 Mon Sep 17 00:00:00 2001 From: tiago Date: Fri, 12 May 2017 20:54:54 -0300 Subject: [PATCH 28/30] Bug 1355663 - Don't de-duplicate tabs from multiple devices. r=eoger MozReview-Commit-ID: AciCTECtXJc --HG-- extra : rebase_source : 82f81f227cf27cf03c4672f0402641010434e74e --- services/sync/modules/SyncedTabs.jsm | 15 ++--------- services/sync/tests/unit/test_syncedtabs.js | 29 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/services/sync/modules/SyncedTabs.jsm b/services/sync/modules/SyncedTabs.jsm index 9d1310d78326..9b01ba7ff630 100644 --- a/services/sync/modules/SyncedTabs.jsm +++ b/services/sync/modules/SyncedTabs.jsm @@ -110,7 +110,6 @@ let SyncedTabsInternal = { let engine = Weave.Service.engineManager.get("tabs"); - let seenURLs = new Set(); let ntabs = 0; for (let client of Object.values(engine.getAllClients())) { @@ -123,24 +122,14 @@ let SyncedTabsInternal = { for (let tab of client.tabs) { let url = tab.urlHistory[0]; log.debug("remote tab", url); - // Note there are some issues with tracking "seen" tabs, including: - // * We really can't return the entire urlHistory record as we are - // only checking the first entry - others might be different. - // * We don't update the |lastUsed| timestamp to reflect the - // most-recently-seen time. - // In a followup we should consider simply dropping this |seenUrls| - // check and return duplicate records - it seems the user will be more - // confused by tabs not showing up on a device (because it was detected - // as a dupe so it only appears on a different device) than being - // confused by seeing the same tab on different clients. - if (!url || seenURLs.has(url)) { + + if (!url) { continue; } let tabRepr = await this._makeTab(client, tab, url, showRemoteIcons); if (filter && !this._tabMatchesFilter(tabRepr, filter)) { continue; } - seenURLs.add(url); clientRepr.tabs.push(tabRepr); } // We return all clients, even those without tabs - the consumer should diff --git a/services/sync/tests/unit/test_syncedtabs.js b/services/sync/tests/unit/test_syncedtabs.js index 9b787b6dd243..43404f42a7fc 100644 --- a/services/sync/tests/unit/test_syncedtabs.js +++ b/services/sync/tests/unit/test_syncedtabs.js @@ -217,3 +217,32 @@ add_task(async function test_filter() { equal(clients[0].tabs.length, 1); equal(clients[0].tabs[0].url, "http://foo.com/"); }); + +add_task(async function test_duplicatesTabsAcrossClients() { + + await configureClients({ + guid_desktop: { + clientName: "My Desktop", + tabs: [ + { + urlHistory: ["http://foo.com/"], + title: "A test page.", + }], + }, + guid_mobile: { + clientName: "My Phone", + tabs: [ + { + urlHistory: ["http://foo.com/"], + title: "A test page.", + }], + }, + }); + + let clients = await SyncedTabs.getTabClients(); + equal(clients.length, 2); + equal(clients[0].tabs.length, 1); + equal(clients[1].tabs.length, 1); + equal(clients[0].tabs[0].url, "http://foo.com/"); + equal(clients[1].tabs[0].url, "http://foo.com/"); +}); From b912f951acf7b91d4e67baaf9e8f184b21d8a432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Mon, 15 May 2017 10:15:37 -0500 Subject: [PATCH 29/30] servo: Merge #16869 - Ignore mime type parameters in nosniff (fixes #16049) (from nox:mime); r=nox Source-Repo: https://github.com/servo/servo Source-Revision: f3c8f7e0d0fb42f32e3699d07fc0f8002bf564c8 --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : d7ddf463cc31fc89f0d289b5739767e2ffff1de7 --- servo/components/net/fetch/methods.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/servo/components/net/fetch/methods.rs b/servo/components/net/fetch/methods.rs index a073056ee56b..b6b5d80231c0 100644 --- a/servo/components/net/fetch/methods.rs +++ b/servo/components/net/fetch/methods.rs @@ -559,10 +559,10 @@ pub fn should_be_blocked_due_to_nosniff(request_type: Type, response_headers: &H mime!(Text / ("x-javascript")), ]; - javascript_mime_types.contains(mime_type) + javascript_mime_types.iter() + .any(|mime| mime.0 == mime_type.0 && mime.1 == mime_type.1) } - let text_css: Mime = mime!(Text / Css); // Assumes str::starts_with is equivalent to mime::TopLevel return match request_type { // Step 6 @@ -575,8 +575,8 @@ pub fn should_be_blocked_due_to_nosniff(request_type: Type, response_headers: &H // Step 7 Type::Style => { match content_type_header { - Some(&ContentType(ref mime_type)) => mime_type != &text_css, - None => true + Some(&ContentType(Mime(TopLevel::Text, SubLevel::Css, _))) => false, + _ => true } } // Step 8 From 05822614743a6ab63cfd93abc6b12e04e9791aef Mon Sep 17 00:00:00 2001 From: Mike Conley Date: Thu, 4 May 2017 12:42:27 -0400 Subject: [PATCH 30/30] Bug 1362103 - Suppress animation on first window opening. r=florian Enabled by default behind the browser.suppress_first_window_animation pref. MozReview-Commit-ID: 4mzy4Qif0LX --HG-- extra : rebase_source : 926dd03514dd1792ee1c9d9a662a4a3657bc6796 --- browser/app/profile/firefox.js | 2 ++ browser/components/nsBrowserContentHandler.js | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 98fc4beba765..cb9022c5bc57 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1651,3 +1651,5 @@ pref("browser.sessionstore.restore_tabs_lazily", true); pref("urlclassifier.malwareTable", "goog-malware-shavar,goog-unwanted-shavar,goog-malware-proto,goog-unwanted-proto,test-malware-simple,test-unwanted-simple"); pref("urlclassifier.phishTable", "goog-phish-shavar,goog-phish-proto,test-phish-simple"); #endif + +pref("browser.suppress_first_window_animation", true); diff --git a/browser/components/nsBrowserContentHandler.js b/browser/components/nsBrowserContentHandler.js index a72b63635956..0d4563ddfda8 100644 --- a/browser/components/nsBrowserContentHandler.js +++ b/browser/components/nsBrowserContentHandler.js @@ -576,7 +576,12 @@ nsBrowserContentHandler.prototype = { // The global PB Service consumes this flag, so only eat it in per-window // PB builds. if (PrivateBrowsingUtils.isInTemporaryAutoStartMode) { - this.mFeatures = ",private"; + this.mFeatures += ",private"; + } + + if (Services.prefs.getBoolPref("browser.suppress_first_window_animation") && + !Services.wm.getMostRecentWindow("navigator:browser")) { + this.mFeatures += ",suppressanimation"; } }