diff --git a/cms/static/js/views/pages/container.js b/cms/static/js/views/pages/container.js
index 543503a598df..cec031076e62 100644
--- a/cms/static/js/views/pages/container.js
+++ b/cms/static/js/views/pages/container.js
@@ -191,7 +191,9 @@ function($, _, Backbone, gettext, BasePage,
xblockWrapper.addClass('is-selected');
break;
default:
- console.warn('Unhandled message type:', data.type);
+ if (data.type) {
+ console.warn('Unhandled message type:', data.type);
+ }
}
});
}
diff --git a/cms/static/js/views/xblock.js b/cms/static/js/views/xblock.js
index adedd2e2c093..9f63b184776e 100644
--- a/cms/static/js/views/xblock.js
+++ b/cms/static/js/views/xblock.js
@@ -58,7 +58,7 @@ function($, _, ViewUtils, BaseView, XBlock, HtmlUtils) {
aside;
fragmentsRendered = this.renderXBlockFragment(fragment, wrapper);
- fragmentsRendered.always(function() {
+ fragmentsRendered.done(function() {
xblockElement = self.$('.xblock').first();
try {
xblock = XBlock.initializeBlock(xblockElement);
@@ -180,13 +180,16 @@ function($, _, ViewUtils, BaseView, XBlock, HtmlUtils) {
var self = this,
applyResource,
numResources,
- deferred;
+ deferred,
+ failedJs = false;
numResources = resources.length;
deferred = $.Deferred();
applyResource = function(index) {
var hash, resource, value, promise;
if (index >= numResources) {
- deferred.resolve();
+ deferred.resolve({
+ failedJs: failedJs
+ });
return;
}
value = resources[index];
@@ -194,6 +197,19 @@ function($, _, ViewUtils, BaseView, XBlock, HtmlUtils) {
if (!window.loadedXBlockResources) {
window.loadedXBlockResources = [];
}
+ if (!window.failedXBlockResources) {
+ window.failedXBlockResources = [];
+ }
+ // A JS resource that previously failed (e.g., blocked by an extension):
+ // mark failedJs so the specific XBlock that needs it gets skipped,
+ // but continue loading the rest of the fragment's resources so that
+ // other XBlocks in the same fragment are not affected.
+ if (_.indexOf(window.failedXBlockResources, hash) >= 0) {
+ failedJs = true;
+ applyResource(index + 1);
+ return;
+ }
+
if (_.indexOf(window.loadedXBlockResources, hash) < 0) {
resource = value[1];
promise = self.loadResource(resource);
@@ -201,8 +217,24 @@ function($, _, ViewUtils, BaseView, XBlock, HtmlUtils) {
promise.done(function() {
applyResource(index + 1);
}).fail(function() {
- deferred.reject();
+ console.warn(
+ 'Failed to load XBlock resource:',
+ resource.data || resource.kind
+ );
+
+ if (resource.mimetype === 'application/javascript') {
+ failedJs = true;
+
+ // Remember this hash so subsequent fragments that reference
+ // the same resource skip it without a network round-trip.
+ window.failedXBlockResources.push(hash);
+ }
+
+ // Always continue — other resources in this fragment may belong
+ // to unrelated XBlocks and must still be loaded.
+ applyResource(index + 1);
});
+
} else {
applyResource(index + 1);
}
@@ -235,7 +267,20 @@ function($, _, ViewUtils, BaseView, XBlock, HtmlUtils) {
// xss-lint: disable=javascript-jquery-append,javascript-concat-html
$head.append('');
} else if (kind === 'url') {
- return ViewUtils.loadJavaScript(data);
+ // Use a raw