Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/components/icon/icon.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,22 @@ describe('MdIcon service', function() {
$scope.$digest();
});

// This covers a case where we saw a g3 test using an empty <svg></svg> and it could
// throw an exception "Looking up elements via selectors is not supported by jqLite!"
// if proper checks weren't in place in the transformClone() code.
it('should handle empty SVGs', function() {
// Just request the icon to be stored in the cache.
$mdIcon('emptyGroup.svg');

$scope.$digest();

$mdIcon('emptyGroup.svg').then(function(el) {
expect(el).toBeTruthy();
});

$scope.$digest();
});

it('should suffix duplicated ids and refs', function() {
// Just request the icon to be stored in the cache.
$mdIcon('angular-logo.svg');
Expand Down
51 changes: 36 additions & 15 deletions src/components/icon/js/iconService.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,14 +350,14 @@ MdIconProvider.prototype = {
/**
* Configuration item stored in the Icon registry; used for lookups
* to load if not already cached in the `loaded` cache
* @param url
* @param viewBoxSize
* @param {string} url
* @param {=number} viewBoxSize
* @constructor
*/
function ConfigurationItem(url, viewBoxSize) {
this.url = url;
this.viewBoxSize = viewBoxSize || config.defaultViewBoxSize;
}
this.url = url;
this.viewBoxSize = viewBoxSize || config.defaultViewBoxSize;
}

/**
* @ngdoc service
Expand Down Expand Up @@ -473,13 +473,13 @@ function MdIconService(config, $templateRequest, $q, $log, $mdUtil, $sce) {
}

/**
* @param {Icon} cacheElement cached icon from the iconCache
* @param {!Icon} cacheElement cached icon from the iconCache
* @returns {Icon} cloned Icon element with unique ids
*/
function transformClone(cacheElement) {
var clone = cacheElement.clone();
var newUid = $mdUtil.nextUid();
var cacheSuffix;
var cacheSuffix, svgElement;

// Verify that the newUid only contains a number and not some XSS content.
if (!isFinite(Number(newUid))) {
Expand All @@ -501,11 +501,19 @@ function MdIconService(config, $templateRequest, $q, $log, $mdUtil, $sce) {
angular.forEach(clone.querySelectorAll('[id]'), function(descendantElem) {
descendantElem.id += cacheSuffix;
});
// Inject the cacheSuffix into all instances of url(id) and xlink:href="#id".
// This use of innerHTML should be safe from XSS attack since we are only injecting the
// cacheSuffix with content from $mdUtil.nextUid which we verify is a finite number above.
clone.innerHTML = clone.innerHTML.replace(/(.*url\(#)(\w*)(\).*)/g, addCacheSuffixToId);
clone.innerHTML = clone.innerHTML.replace(/(.*xlink:href="#)(\w*)(".*)/g, addCacheSuffixToId);
// innerHTML of SVG elements is not supported by IE11
if (clone.innerHTML === undefined) {
svgElement = $mdUtil.getInnerHTML(clone);
svgElement = svgElement.replace(/(.*url\(#)(\w*)(\).*)/g, addCacheSuffixToId);
svgElement = svgElement.replace(/(.*xlink:href="#)(\w*)(".*)/g, addCacheSuffixToId);
clone = angular.element(svgElement)[0];
} else {
// Inject the cacheSuffix into all instances of url(id) and xlink:href="#id".
// This use of innerHTML should be safe from XSS attack since we are only injecting the
// cacheSuffix with content from $mdUtil.nextUid which we verify is a finite number above.
clone.innerHTML = clone.innerHTML.replace(/(.*url\(#)(\w*)(\).*)/g, addCacheSuffixToId);
clone.innerHTML = clone.innerHTML.replace(/(.*xlink:href="#)(\w*)(".*)/g, addCacheSuffixToId);
}

return clone;
}
Expand Down Expand Up @@ -605,24 +613,37 @@ function MdIconService(config, $templateRequest, $q, $log, $mdUtil, $sce) {

/**
* Check target signature to see if it is an Icon instance.
* @param {Icon|Element} target
* @returns {boolean} true if the specified target is an Icon object, false otherwise.
*/
function isIcon(target) {
return angular.isDefined(target.element) && angular.isDefined(target.config);
}

/**
* Define the Icon class
* Define the Icon class
* @param {Element} el
* @param {=ConfigurationItem} config
* @constructor
*/
function Icon(el, config) {
var elementContents;
// If the node is a <symbol>, it won't be rendered so we have to convert it into <svg>.
if (el && el.tagName.toLowerCase() === 'symbol') {
var viewbox = el.getAttribute('viewBox');
el = angular.element('<svg xmlns="http://www.w3.org/2000/svg">').html(el.innerHTML)[0];
// Check if innerHTML is supported as IE11 does not support innerHTML on SVG elements.
if (el.innerHTML) {
elementContents = el.innerHTML;
} else {
elementContents = $mdUtil.getInnerHTML(el);
}
el = angular.element('<svg xmlns="http://www.w3.org/2000/svg">').append(elementContents)[0];
if (viewbox) el.setAttribute('viewBox', viewbox);
}

if (el && el.tagName.toLowerCase() !== 'svg') {
el = angular.element('<svg xmlns="http://www.w3.org/2000/svg">').append(el.cloneNode(true))[0];
el = angular.element(
'<svg xmlns="http://www.w3.org/2000/svg">').append(el.cloneNode(true))[0];
}

// Inject the namespace if not available...
Expand Down
39 changes: 26 additions & 13 deletions src/core/util/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ var nextUniqueId = 0;
* Util
*/
angular
.module('material.core')
.factory('$mdUtil', UtilFactory);
.module('material.core')
.factory('$mdUtil', UtilFactory);

/**
* @ngInject
Expand Down Expand Up @@ -128,7 +128,7 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in
// or a clientRect: a rect relative to the page
var offsetRect = isOffsetRect ?
offsetParent.getBoundingClientRect() :
{left: 0, top: 0, width: 0, height: 0};
{left: 0, top: 0, width: 0, height: 0};
return {
left: nodeRect.left - offsetRect.left,
top: nodeRect.top - offsetRect.top,
Expand Down Expand Up @@ -857,6 +857,19 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in
return array.filter(function(value, index, self) {
return self.indexOf(value) === index;
});
},

/**
* Function to get innerHTML of SVG and Symbol elements in IE11
* @param {Element} element
* @returns {string} the innerHTML of the element passed in
*/
getInnerHTML: function(element) {
var serializer = new XMLSerializer();

return Array.prototype.map.call(element.childNodes, function (child) {
return serializer.serializeToString(child);
}).join('');
}
};

Expand All @@ -879,14 +892,14 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in
*/

angular.element.prototype.focus = angular.element.prototype.focus || function() {
if (this.length) {
this[0].focus();
}
return this;
};
if (this.length) {
this[0].focus();
}
return this;
};
angular.element.prototype.blur = angular.element.prototype.blur || function() {
if (this.length) {
this[0].blur();
}
return this;
};
if (this.length) {
this[0].blur();
}
return this;
};