Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix(compile): do not create directives for constant media URL attributes #16737

Merged
merged 3 commits into from
Nov 20, 2018

Conversation

petebacondarwin
Copy link
Contributor

@petebacondarwin petebacondarwin commented Oct 18, 2018

By creating attribute directives that watch the value of
media url attributes (e.g. img[src]) we caused a conflict
when both src and data-src were appearing on the
same element. As each directive was trying to write to the
attributes on the element, where AngularJS treats src and
data-src as synonymous.

This commit ensures that we do not create create such directives
when the media url attribute is a constant (no interpolation).

Because of this (and because we no longer sanitize URLs in the
$attr.$set() method, this commit also updates ngHref and
ngSrc to do a preliminary sanitization of URLs in case there
is no interpolation in the attribute value.

@googlebot
Copy link

☹️ Sorry, but only Googlers may change the label cla: yes.

@@ -3871,6 +3871,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
compile: function() {
return {
pre: function attrInterpolatePreLinkFn(scope, element, attr) {
if (trustedContext === $sce.MEDIA_URL && isConstantInterpolation(interpolateFn)) {
// Constant MEDIA_URL attributes should just be sanitized and not watched.
attr[name] = interpolateFn(scope);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this assignment? I thought to go back to the previous behavior we'd want to do no assignments.

I thought we'd just update this condition to return early like it previously did...

Copy link
Contributor Author

@petebacondarwin petebacondarwin Oct 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the only way to keep all the tests passing. If you change the condition you linked to then a load of other ngSrc etc related tests fail. This is due to the fact that the url sanitization has moved to the interpolation rather than being done in the compiler.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh :/

And we can't do this if + return outside the directive? Before doing directives.push...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No because we need to set the sanitized value to the attribute at least once.

@fbrandel
Copy link

fbrandel commented Oct 22, 2018

When I run the fix locally, it seems that the ng-attr-data-src directive on images now stopped working. The attribute is not interpolated/watched, but it should. Unfortunately I was not able to provide a test case for this, just noticed it by manually trying it out.

The original reported bug was fixed though :)

@petebacondarwin
Copy link
Contributor Author

@fbrandel - I don't think what you are suggesting ever worked with Angular. The following variants on ng-attr work: ng-attr-src and data-ng-attr-src but ng-attr-data-src does not work. See http://next.plnkr.co/edit/sbrIYJYyKE0S7XVA?preview

@fbrandel
Copy link

@petebacondarwin ng-attr-data-src should bind to the data-src attribute, not the src attribute. So the plunkr shows no image (which is correct) but an interpolated data-src attribute.

With the PR applied this stopped working, as no data-src attribute is present on the element.

@petebacondarwin
Copy link
Contributor Author

Ahah! Let me see if I can reproduce in a unit test...

@petebacondarwin
Copy link
Contributor Author

@fbrandel I added unit tests for this case but I cannot reproduce your issue. Please can you take a look and see if you can do so?

@fbrandel
Copy link

fbrandel commented Oct 23, 2018

@petebacondarwin After building the project with the PR applied, I can see the tests pass.

@fbrandel
Copy link

@petebacondarwin Sorry to come up with more investigation ;) It seems that the combination of src and ng-attr-data-src attributes on an image now behaves differently. With a src attribute set on the image, the data-src does not contain the value of the ng-attr-data-src expression but the value of src.

Plunkr: https://next.plnkr.co/edit/mMn0sv47O81Hi0FJ

Unit Test:

it('should compile img with constant [src]-attribute and [ng-attr-data-src] attribute', inject(function() {
        $rootScope.name = 'some-image.png';
        element = $compile('<img src="constant.png" ng-attr-data-src="{{name}}">')($rootScope);
        expect(element.attr('data-src')).toBeUndefined();

        $rootScope.$digest();
        expect(element.attr('src')).toBe('constant.png');
        expect(element.attr('data-src')).toBe('some-image.png');

        $rootScope.name = 'other-image.png';
        $rootScope.$digest();
        expect(element.attr('src')).toBe('constant.png');
        expect(element.attr('data-src')).toBe('other-image.png');
}));

@petebacondarwin
Copy link
Contributor Author

Hmm, oh dear. This is a can of worms...

@petebacondarwin petebacondarwin changed the title fix(compile): do not watch constant media attributes fix(compile): do not create direcrtives for constant media URL attributes Oct 24, 2018
@petebacondarwin petebacondarwin changed the title fix(compile): do not create direcrtives for constant media URL attributes fix(compile): do not create directives for constant media URL attributes Oct 24, 2018
@petebacondarwin
Copy link
Contributor Author

@fbrandel - can you take a look at this alternative approach? See if you can break this one :-)

@fbrandel
Copy link

@petebacondarwin Looking good with my scenarios! Thank you so far for taking care!

@@ -12109,6 +12118,47 @@ describe('$compile', function() {
expect(element.attr('dash-test4')).toBe('JamieMason');
}));

it('should work with img[src]', inject(function() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It tests ng-attr-src, not src. The same remark for the next test.

@Narretz
Copy link
Contributor

Narretz commented Oct 25, 2018

We are now setting the attribute twice, even for interpolated attributes, which is not that great imo. Can we detect if the attribute value has not been interpolated?

@@ -422,6 +422,10 @@ forEach(['src', 'srcset', 'href'], function(attrName) {
propName = null;
}

// We need to sanitize the url at least once, in case it is a constant
// non-interpolated attribute.
attr.$set(normalized, $sce.getTrustedMediaUrl(attr[normalized]));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could check the current value and only call $set() if getTrustedMediaUrl() return a different value.

…ributes

By creating attribute directives that watch the value of
media url attributes (e.g. `img[src]`) we caused a conflict
when both `src` and `data-src` were appearing on the
same element. As each directive was trying to write to the
attributes on the element, where AngularJS treats `src` and
`data-src` as synonymous.

This commit ensures that we do not create create such directives
when the media url attribute is a constant (no interpolation).

Because of this (and because we no longer sanitize URLs in the
`$attr.$set()` method, this commit also updates `ngHref` and
`ngSrc` to do a preliminary sanitization of URLs in case there
is no interpolation in the attribute value.

Fixes angular#16734
@petebacondarwin petebacondarwin merged commit 0687a4f into angular:master Nov 20, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants