Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #8869: Add innerHTML lint check #8870

Merged
merged 10 commits into from
Mar 28, 2020
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@
/core/domain/change_domain.py @ankita240796
/core/templates/tests/ @kevinlee12 @nithusha21
/core/templates/domain/utilities/ @bansalnitish
/core/templates/Polyfills.ts @ankita240796
/schema_utils*.py @seanlip
/utils*.py @aks681
/python_utils*.py @kevinlee12
Expand Down
134 changes: 2 additions & 132 deletions core/templates/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ require('google-analytics.initializer.ts');
// loaded after app.constants.ts
require('I18nFooter.ts');

require('Polyfills.ts');

const sourceMappedStackTrace = require('sourcemapped-stacktrace');

angular.module('oppia').config([
Expand Down Expand Up @@ -303,135 +305,3 @@ angular.module('oppia').factory('$exceptionHandler', [
};
}
]);

// Add a String.prototype.trim() polyfill for IE8.
if (typeof String.prototype.trim !== 'function') {
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, '');
};
}

// Add an Object.create() polyfill for IE8.
if (typeof Object.create !== 'function') {
(function() {
var F = function() {};
Object.create = function(o) {
if (arguments.length > 1) {
throw Error('Second argument for Object.create() is not supported');
}
if (o === null) {
throw Error('Cannot set a null [[Prototype]]');
}
if (typeof o !== 'object') {
throw TypeError('Argument must be an object');
}
F.prototype = o;
return new F();
};
})();
}

// Add a Number.isInteger() polyfill for IE.
Number.isInteger = Number.isInteger || function(value) {
return (
typeof value === 'number' && isFinite(value) &&
Math.floor(value) === value);
};


// Add Array.fill() polyfill for IE.
if (!Array.prototype.fill) {
Object.defineProperty(Array.prototype, 'fill', {
value: function(value) {
// Steps 1-2.
if (this === null) {
throw new TypeError('this is null or not defined');
}

var O = Object(this);

// Steps 3-5.
var len = O.length >>> 0;

// Steps 6-7.
var start = arguments[1];
var relativeStart = start >> 0;

// Step 8.
var k = relativeStart < 0 ?
Math.max(len + relativeStart, 0) :
Math.min(relativeStart, len);

// Steps 9-10.
var end = arguments[2];
var relativeEnd = end === undefined ?
len : end >> 0;

// Step 11.
var final = relativeEnd < 0 ?
Math.max(len + relativeEnd, 0) :
Math.min(relativeEnd, len);

// Step 12.
while (k < final) {
O[k] = value;
k++;
}

// Step 13.
return O;
}
});
}


// Add SVGElement.prototype.outerHTML polyfill for IE
if (!('outerHTML' in SVGElement.prototype)) {
Object.defineProperty(SVGElement.prototype, 'outerHTML', {
get: function() {
var $node, $temp;
$temp = document.createElement('div');
$node = this.cloneNode(true);
$temp.appendChild($node);
return $temp.innerHTML;
},
enumerable: false,
configurable: true
});
}


// Older browsers might not implement mediaDevices at all,
// so we set an empty object first.
if (navigator.mediaDevices === undefined) {
// @ts-ignore: mediaDevices is read-only error.
navigator.mediaDevices = {};
}

// Some browsers partially implement mediaDevices.
// We can't just assign an object with getUserMedia
// as it would overwrite existing properties.
// Here, we will just add the getUserMedia property
// if it's missing.
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function(constraints) {
// First get ahold of the legacy getUserMedia, if present.
var getUserMedia = (
// @ts-ignore: 'webkitGetUserMedia' and 'mozGetUserMedia'
// property does not exist error.
navigator.webkitGetUserMedia || navigator.mozGetUserMedia);

// If getUserMedia is not implemented, return a rejected promise
// with an error to keep a consistent interface.
if (!getUserMedia) {
return Promise.reject(
new Error('getUserMedia is not implemented in this browser'));
}

// Otherwise, wrap the call to the old navigator.getUserMedia
// with a Promise.
return new Promise(function(resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject);
});
};
}
149 changes: 149 additions & 0 deletions core/templates/Polyfills.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright 2020 The Oppia Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
* @fileoverview Polyfills for Oppia.
*/

// Add a String.prototype.trim() polyfill for IE8.
if (typeof String.prototype.trim !== 'function') {
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, '');
};
}

// Add an Object.create() polyfill for IE8.
if (typeof Object.create !== 'function') {
(function() {
var F = function() {};
Object.create = function(o) {
if (arguments.length > 1) {
throw Error('Second argument for Object.create() is not supported');
}
if (o === null) {
throw Error('Cannot set a null [[Prototype]]');
}
if (typeof o !== 'object') {
throw TypeError('Argument must be an object');
}
F.prototype = o;
return new F();
};
})();
}

// Add a Number.isInteger() polyfill for IE.
Number.isInteger = Number.isInteger || function(value) {
return (
typeof value === 'number' && isFinite(value) &&
Math.floor(value) === value);
};


// Add Array.fill() polyfill for IE.
if (!Array.prototype.fill) {
Object.defineProperty(Array.prototype, 'fill', {
value: function(value) {
// Steps 1-2.
if (this === null) {
throw new TypeError('this is null or not defined');
}

var O = Object(this);

// Steps 3-5.
var len = O.length >>> 0;

// Steps 6-7.
var start = arguments[1];
var relativeStart = start >> 0;

// Step 8.
var k = relativeStart < 0 ?
Math.max(len + relativeStart, 0) :
Math.min(relativeStart, len);

// Steps 9-10.
var end = arguments[2];
var relativeEnd = end === undefined ?
len : end >> 0;

// Step 11.
var final = relativeEnd < 0 ?
Math.max(len + relativeEnd, 0) :
Math.min(relativeEnd, len);

// Step 12.
while (k < final) {
O[k] = value;
k++;
}

// Step 13.
return O;
}
});
}


// Add SVGElement.prototype.outerHTML polyfill for IE
if (!('outerHTML' in SVGElement.prototype)) {
Object.defineProperty(SVGElement.prototype, 'outerHTML', {
get: function() {
var $node, $temp;
$temp = document.createElement('div');
$node = this.cloneNode(true);
$temp.appendChild($node);
return $temp.innerHTML;
},
enumerable: false,
configurable: true
});
}


// Older browsers might not implement mediaDevices at all,
// so we set an empty object first.
if (navigator.mediaDevices === undefined) {
// @ts-ignore: mediaDevices is read-only error.
navigator.mediaDevices = {};
}

// Some browsers partially implement mediaDevices.
// We can't just assign an object with getUserMedia
// as it would overwrite existing properties.
// Here, we will just add the getUserMedia property
// if it's missing.
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function(constraints) {
// First get ahold of the legacy getUserMedia, if present.
var getUserMedia = (
// @ts-ignore: 'webkitGetUserMedia' and 'mozGetUserMedia'
// property does not exist error.
navigator.webkitGetUserMedia || navigator.mozGetUserMedia);

// If getUserMedia is not implemented, return a rejected promise
// with an error to keep a consistent interface.
if (!getUserMedia) {
return Promise.reject(
new Error('getUserMedia is not implemented in this browser'));
}

// Otherwise, wrap the call to the old navigator.getUserMedia
// with a Promise.
return new Promise(function(resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject);
});
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,12 @@ export class ExtractImageFilenamesFromStateService {
_extractFilepathValueFromOppiaNonInteractiveImageTag(
strHtml: string): Array<any> {
let filenames = [];
let dummyElement = document.createElement('div');
dummyElement.innerHTML = (
let unescapedHtmlString = (
this.htmlEscaperService.escapedStrToUnescapedStr(strHtml));
let dummyDocument = (
new DOMParser().parseFromString(unescapedHtmlString, 'text/html'));

let imageTagList = dummyElement.getElementsByTagName(
let imageTagList = dummyDocument.getElementsByTagName(
'oppia-noninteractive-image');
for (let i = 0; i < imageTagList.length; i++) {
// We have the attribute of filepath in oppia-noninteractive-image tag.
Expand Down
3 changes: 2 additions & 1 deletion extensions/interactions/CodeRepl/protractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
* Editor.
*/

var waitFor = require('../../../core/tests/protractor_utils/waitFor.js');
var waitFor = require(
process.cwd() + '/core/tests/protractor_utils/waitFor.js');

var customizeInteraction = function(interactionEditor, placeHolderText) {
browser.executeScript(
Expand Down
5 changes: 3 additions & 2 deletions extensions/interactions/Continue/protractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
* interaction.
*/

var objects = require('../../objects/protractor.js');
var waitFor = require('../../../core/tests/protractor_utils/waitFor.js');
var objects = require(process.cwd() + '/extensions/objects/protractor.js');
var waitFor = require(
process.cwd() + '/core/tests/protractor_utils/waitFor.js');

var customizeInteraction = function(elem, buttonText) {
if (buttonText) {
Expand Down
2 changes: 1 addition & 1 deletion extensions/interactions/EndExploration/protractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* interaction.
*/

var objects = require('../../objects/protractor.js');
var objects = require(process.cwd() + '/extensions/objects/protractor.js');
/**
* Add recommended exploration Id to End Exploration interaction.
* @param {Object} elem - The Customize Exploration modal for End Exploration.
Expand Down
2 changes: 1 addition & 1 deletion extensions/interactions/FractionInput/protractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* @fileoverview End-to-end testing utilities for the Fraction interaction.
*/

var objects = require('../../objects/protractor.js');
var objects = require(process.cwd() + '/extensions/objects/protractor.js');

var customizeInteraction = function(elem, requireSimplestForm) {
objects.BooleanEditor(elem.element(by.tagName('schema-based-bool-editor')))
Expand Down
6 changes: 3 additions & 3 deletions extensions/interactions/GraphInput/protractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
* @fileoverview End-to-end testing utilities for the GraphInput interaction.
*/

var forms = require('../../../core/tests/protractor_utils/forms.js');
var objects = require('../../objects/protractor.js');
var waitFor = require('../../../core/tests/protractor_utils/waitFor.js');
var forms = require(process.cwd() + '/core/tests/protractor_utils/forms.js');
var waitFor = require(
process.cwd() + '/core/tests/protractor_utils/waitFor.js');

var customizeInteraction = function(interactionEditor, graphDict) {
var graphInputContainer = interactionEditor.element(by.css(
Expand Down
2 changes: 0 additions & 2 deletions extensions/interactions/LogicProof/protractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
* @fileoverview End-to-end testing utilities for the LogicProof interaction.
*/

var objects = require('../../objects/protractor.js');

var customizeInteraction = function(elem, assumption, target, defaultText) {
elem.element(by.id('logicQuestionAssumptions')).sendKeys(assumption);
elem.element(by.id('logicQuestionTarget')).sendKeys(target);
Expand Down
2 changes: 0 additions & 2 deletions extensions/interactions/MathExpressionInput/protractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
* interaction
*/

var objects = require('../../objects/protractor.js');

var mathExpressionInputTag = function(parentElement) {
return parentElement.element(by.tagName(
'oppia-interactive-math-expression-input'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* interaction.
*/

var forms = require('../../../core/tests/protractor_utils/forms.js');
var forms = require(process.cwd() + '/core/tests/protractor_utils/forms.js');

// The members of richTextInstructionsArray are functions, one for each option,
// which will each be passed a 'handler' that they can use to edit the
Expand Down
Loading