Skip to content

Commit 057a5f9

Browse files
Encode urls for css background images (#7906)
* Add new utility to encode urls. Use the encode urls utility to encode all background images in css * need a commit to pull exampleimagery from * skip and fix on otherside --------- Co-authored-by: John Hill <[email protected]>
1 parent 078cd34 commit 057a5f9

File tree

9 files changed

+140
-16
lines changed

9 files changed

+140
-16
lines changed

e2e/test-data/rick space roll.jpg

10 KB
Loading

e2e/tests/functional/plugins/imagery/exampleImagery.e2e.spec.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,6 @@ test.describe('Example Imagery Object', () => {
9696
expect(newPage.url()).toContain('.jpg');
9797
});
9898

99-
// this requires CORS to be enabled in some fashion
100-
test.fixme('Can right click on image and save it as a file', async ({ page }) => {});
101-
10299
test('Can adjust image brightness/contrast by dragging the sliders', async ({
103100
page,
104101
browserName
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*****************************************************************************
2+
* Open MCT, Copyright (c) 2014-2024, United States Government
3+
* as represented by the Administrator of the National Aeronautics and Space
4+
* Administration. All rights reserved.
5+
*
6+
* Open MCT is licensed under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
* http://www.apache.org/licenses/LICENSE-2.0.
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
* License for the specific language governing permissions and limitations
15+
* under the License.
16+
*
17+
* Open MCT includes source code licensed under additional open source
18+
* licenses. See the Open Source Licenses file (LICENSES.md) included with
19+
* this source code distribution or the Licensing information page available
20+
* at runtime from the About dialog for additional information.
21+
*****************************************************************************/
22+
23+
/*
24+
* This test suite verifies modifying the image location of the example imagery object.
25+
*/
26+
27+
import { createDomainObjectWithDefaults } from '../../../../appActions.js';
28+
import { expect, test } from '../../../../pluginFixtures.js';
29+
30+
test.describe('Example Imagery Object Custom Images', () => {
31+
let exampleImagery;
32+
test.beforeEach(async ({ page }) => {
33+
//Go to baseURL
34+
await page.goto('./', { waitUntil: 'domcontentloaded' });
35+
36+
// Create a default 'Example Imagery' object
37+
exampleImagery = await createDomainObjectWithDefaults(page, {
38+
name: 'Example Imagery',
39+
type: 'Example Imagery'
40+
});
41+
42+
// Verify that the created object is focused
43+
await expect(page.locator('.l-browse-bar__object-name')).toContainText(exampleImagery.name);
44+
await page.getByLabel('Focused Image Element').hover({ trial: true });
45+
46+
// Wait for image thumbnail auto-scroll to complete
47+
await expect(page.getByLabel('Image Thumbnail from').last()).toBeInViewport();
48+
});
49+
// this requires CORS to be enabled in some fashion
50+
test.fixme('Can right click on image and save it as a file', async ({ page }) => {});
51+
test('Can provide a custom image location for the example imagery object', async ({ page }) => {
52+
// Modify Example Imagery to create a really stable image which will never let us down
53+
await page.getByRole('button', { name: 'More actions' }).click();
54+
await page.getByRole('menuitem', { name: 'Edit Properties...' }).click();
55+
await page
56+
.locator('#imageLocation-textarea')
57+
.fill(
58+
'https://raw.githubusercontent.com/nasa/openmct/554f77c42fec81cf0f63e62b278012cb08d82af9/e2e/test-data/rick.jpg,https://raw.githubusercontent.com/nasa/openmct/554f77c42fec81cf0f63e62b278012cb08d82af9/e2e/test-data/rick.jpg'
59+
);
60+
await page.getByRole('button', { name: 'Save' }).click();
61+
await page.reload({ waitUntil: 'domcontentloaded' });
62+
63+
// Wait for the thumbnails to finish their scroll animation
64+
// (Wait until the rightmost thumbnail is in view)
65+
await expect(page.getByLabel('Image Thumbnail from').last()).toBeInViewport();
66+
67+
await expect(page.getByLabel('Image Wrapper')).toBeVisible();
68+
});
69+
test.fixme('Can provide a custom image with spaces in name', async ({ page }) => {
70+
test.info().annotations.push({
71+
type: 'issue',
72+
description: 'https://github.com/nasa/openmct/issues/7903'
73+
});
74+
await page.goto(exampleImagery.url, { waitUntil: 'domcontentloaded' });
75+
76+
// Modify Example Imagery to create a really stable image which will never let us down
77+
await page.getByRole('button', { name: 'More actions' }).click();
78+
await page.getByRole('menuitem', { name: 'Edit Properties...' }).click();
79+
await page
80+
.locator('#imageLocation-textarea')
81+
.fill(
82+
'https://raw.githubusercontent.com/nasa/openmct/d8c64f183400afb70137221fc1a035e091bea912/e2e/test-data/rick%20space%20roll.jpg'
83+
);
84+
await page.getByRole('button', { name: 'Save' }).click();
85+
await page.reload({ waitUntil: 'domcontentloaded' });
86+
87+
// Wait for the thumbnails to finish their scroll animation
88+
// (Wait until the rightmost thumbnail is in view)
89+
await expect(page.getByLabel('Image Thumbnail from').last()).toBeInViewport();
90+
91+
await expect(page.getByLabel('Image Wrapper')).toBeVisible();
92+
});
93+
});

src/plugins/condition/components/inspector/StyleEditor.vue

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,7 @@
2828
{ 'is-style-invisible': styleItem.style && styleItem.style.isStyleInvisible },
2929
{ 'c-style-thumb--mixed': mixedStyles.indexOf('backgroundColor') > -1 }
3030
]"
31-
:style="[
32-
styleItem.style.imageUrl
33-
? { backgroundImage: 'url(' + styleItem.style.imageUrl + ')' }
34-
: itemStyle
35-
]"
31+
:style="[encodedImageUrl ? { backgroundImage: 'url(' + encodedImageUrl + ')' } : itemStyle]"
3632
class="c-style-thumb"
3733
>
3834
<span
@@ -62,7 +58,7 @@
6258
@change="updateStyleValue"
6359
/>
6460
<ToolbarButton
65-
v-if="hasProperty(styleItem.style.imageUrl)"
61+
v-if="hasProperty(encodedImageUrl)"
6662
class="c-style__toolbar-button--image-url"
6763
:options="imageUrlOption"
6864
@change="updateStyleValue"
@@ -93,6 +89,8 @@ import ToolbarButton from '@/ui/toolbar/components/ToolbarButton.vue';
9389
import ToolbarColorPicker from '@/ui/toolbar/components/ToolbarColorPicker.vue';
9490
import ToolbarToggleButton from '@/ui/toolbar/components/ToolbarToggleButton.vue';
9591
92+
import { encode_url } from '../../../../utils/encoding';
93+
9694
export default {
9795
name: 'StyleEditor',
9896
components: {
@@ -183,11 +181,14 @@ export default {
183181
},
184182
property: 'imageUrl',
185183
formKeys: ['url'],
186-
value: { url: this.styleItem.style.imageUrl },
184+
value: { url: this.encodedImageUrl },
187185
isEditing: this.isEditing,
188186
nonSpecific: this.mixedStyles.indexOf('imageUrl') > -1
189187
};
190188
},
189+
encodedImageUrl() {
190+
return encode_url(this.styleItem.style.imageUrl);
191+
},
191192
isStyleInvisibleOption() {
192193
return {
193194
value: this.styleItem.style.isStyleInvisible,

src/plugins/displayLayout/components/ImageView.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
</template>
3636

3737
<script>
38+
import { encode_url } from '../../../utils/encoding';
3839
import conditionalStylesMixin from '../mixins/objectStyles-mixin.js';
3940
import LayoutFrame from './LayoutFrame.vue';
4041
@@ -80,12 +81,12 @@ export default {
8081
return this.isEditing || !this.itemStyle?.isStyleInvisible;
8182
},
8283
style() {
83-
let backgroundImage = 'url(' + this.item.url + ')';
84+
let backgroundImage = `url('${encode_url(this.item.url)}')`;
8485
let border = '1px solid ' + this.item.stroke;
8586
8687
if (this.itemStyle) {
8788
if (this.itemStyle.imageUrl !== undefined) {
88-
backgroundImage = 'url(' + this.itemStyle.imageUrl + ')';
89+
backgroundImage = `url('${encode_url(this.itemStyle.imageUrl)}')`;
8990
}
9091
9192
border = this.itemStyle.border;

src/plugins/imagery/components/ImageThumbnail.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
<img
3939
ref="img"
4040
class="c-thumb__image"
41-
:src="`${image.thumbnailUrl || image.url}`"
41+
:src="imageSrc"
4242
fetchpriority="low"
4343
@load="imageLoadCompleted"
4444
/>
@@ -54,6 +54,8 @@
5454
</template>
5555

5656
<script>
57+
import { encode_url } from '../../../utils/encoding';
58+
5759
const THUMB_PADDING = 4;
5860
const BORDER_WIDTH = 2;
5961
@@ -96,6 +98,9 @@ export default {
9698
};
9799
},
98100
computed: {
101+
imageSrc() {
102+
return `${encode_url(this.image.thumbnailUrl) || encode_url(this.image.url)}`;
103+
},
99104
ariaLabel() {
100105
return `Image thumbnail from ${this.image.formattedTime}${this.showAnnotationIndicator ? ', has annotations' : ''}`;
101106
},

src/plugins/imagery/components/ImageryView.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ import { TIME_CONTEXT_EVENTS } from '@/api/time/constants.js';
222222
import imageryData from '@/plugins/imagery/mixins/imageryData.js';
223223
import { VIEW_LARGE_ACTION_KEY } from '@/plugins/viewLargeAction/viewLargeAction.js';
224224
225+
import { encode_url } from '../../../utils/encoding';
225226
import eventHelpers from '../lib/eventHelpers.js';
226227
import AnnotationsCanvas from './AnnotationsCanvas.vue';
227228
import Compass from './Compass/CompassComponent.vue';
@@ -364,7 +365,7 @@ export default {
364365
filter: `brightness(${this.filters.brightness}%) contrast(${this.filters.contrast}%)`,
365366
backgroundImage: `${
366367
this.imageUrl
367-
? `url(${this.imageUrl}),
368+
? `url(${encode_url(this.imageUrl)}),
368369
repeating-linear-gradient(
369370
45deg,
370371
transparent,
@@ -789,7 +790,7 @@ export default {
789790
},
790791
getVisibleLayerStyles(layer) {
791792
return {
792-
backgroundImage: `url(${layer.source})`,
793+
backgroundImage: `url(${encode_url(layer.source)})`,
793794
transform: `scale(${this.zoomFactor}) translate(${this.imageTranslateX / 2}px, ${
794795
this.imageTranslateY / 2
795796
}px)`,

src/ui/layout/AppLogo.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,15 @@
3232
<script>
3333
import mount from 'utils/mount';
3434
35+
import { encode_url } from '../../utils/encoding';
3536
import AboutDialog from './AboutDialog.vue';
3637
3738
export default {
3839
inject: ['openmct'],
3940
mounted() {
4041
const branding = this.openmct.branding();
4142
if (branding.smallLogoImage) {
42-
this.$refs.aboutLogo.style.backgroundImage = `url('${branding.smallLogoImage}')`;
43+
this.$refs.aboutLogo.style.backgroundImage = `url('${encode_url(branding.smallLogoImage)}')`;
4344
}
4445
},
4546
methods: {

src/utils/encoding.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*****************************************************************************
2+
* Open MCT, Copyright (c) 2014-2024, United States Government
3+
* as represented by the Administrator of the National Aeronautics and Space
4+
* Administration. All rights reserved.
5+
*
6+
* Open MCT is licensed under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
* http://www.apache.org/licenses/LICENSE-2.0.
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
* License for the specific language governing permissions and limitations
15+
* under the License.
16+
*
17+
* Open MCT includes source code licensed under additional open source
18+
* licenses. See the Open Source Licenses file (LICENSES.md) included with
19+
* this source code distribution or the Licensing information page available
20+
* at runtime from the About dialog for additional information.
21+
*****************************************************************************/
22+
23+
export function encode_url(url) {
24+
return url ? encodeURI(url) : url;
25+
}

0 commit comments

Comments
 (0)