Web Inspector: Local Resource Overrides: automatically create an image/font local override when dragging content over a non-overridden resource
https://bugs.webkit.org/show_bug.cgi?id=202957
Reviewed by Joseph Pecoraro.
Since non-text resources aren't editable, some users of local resource overrides kept trying
to drag image/font files over the actual resource content view (not the local override),
which didn't do anything. Rather than having to click "Create Local Override" and then do a
drag/drop, we should support the "shorter" workflow of drag/drop over the actual resource.
* UserInterface/Base/FileUtilities.js:
(WI.FileUtilities.import): Added.
(WI.FileUtilities.importText):
(WI.FileUtilities.importJSON):
(WI.FileUtilities.importData): Added.
(WI.FileUtilities.async readText):
(WI.FileUtilities.async readJSON):
(WI.FileUtilities.async readData): Added.
(WI.FileUtilities.async _read): Added.
Create utility functions for importing non-text content as data.
Drive-by: fix a bug in the `import*` functions where the `callback` would be bound on the
first call, meaning that since the `<input>` was cached for all calls, we'd only
ever use the first `callback` in subsequent calls.
* UserInterface/Views/DropZoneView.js:
(WI.DropZoneView):
(WI.DropZoneView.prototype.set text): Added.
Support the text content of the drop zone changing after it's initialized.
* UserInterface/Views/FontResourceContentView.js:
(WI.FontResourceContentView):
(WI.FontResourceContentView.prototype.contentAvailable):
(WI.FontResourceContentView.prototype.dropZoneHandleDragEnter): Added.
(WI.FontResourceContentView.prototype.dropZoneHandleDrop):
(WI.FontResourceContentView.prototype._handleLocalResourceContentDidChange): Added.
* UserInterface/Views/ImageResourceContentView.js:
(WI.ImageResourceContentView):
(WI.ImageResourceContentView.prototype.contentAvailable):
(WI.ImageResourceContentView.prototype.dropZoneHandleDragEnter): Added.
(WI.ImageResourceContentView.prototype.dropZoneHandleDrop):
(WI.ImageResourceContentView.prototype._handleLocalResourceContentDidChange): Added.
Support drag/drop on non-override image/font content views, which will create/update the
local resource override for that resource.
* UserInterface/Views/ResourceContentView.js:
(WI.ResourceContentView):
(WI.ResourceContentView.prototype.get navigationItems):
(WI.ResourceContentView.prototype._handleImportLocalResourceOverride): Added.
When viewing a local resource override, add an "Import" navigation item for non-drag/drop
updating of the contents of the local resource override. This is also exposed for text-based
local resource overrides, since drag/drop inserts the contents of the file (if it's text),
which attempts to determine whether the dropped file is text or data based on the MIME type.
* UserInterface/Views/AuditNavigationSidebarPanel.js:
(WI.AuditNavigationSidebarPanel.prototype._handleImportButtonNavigationItemClicked):
* UserInterface/Views/CanvasOverviewContentView.js:
(WI.CanvasOverviewContentView.prototype._handleImportButtonNavigationItemClicked):
* UserInterface/Views/CanvasSidebarPanel.js:
(WI.CanvasSidebarPanel.prototype._handleImportButtonNavigationItemClicked):
* UserInterface/Views/HeapAllocationsTimelineView.js:
(WI.HeapAllocationsTimelineView.prototype._importButtonNavigationItemClicked):
* UserInterface/Views/NetworkTableContentView.js:
(WI.NetworkTableContentView.prototype._importHAR):
* UserInterface/Views/TimelineRecordingContentView.js:
(WI.TimelineRecordingContentView.prototype._importButtonNavigationItemClicked):
Explicitly allow multiple files to be imported at the same time.
* UserInterface/Base/BlobUtilities.js:
(WI.BlobUtilities.blobForContent):
Drive-by: remove extra `base64Encoded` argument when calling `decodeBase64ToBlob`, which
caused SVG-based image local resource overrides to show a broken image when
closing and reopening Web Inspector.
* Localizations/en.lproj/localizedStrings.js:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251144 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebInspectorUI/ChangeLog b/Source/WebInspectorUI/ChangeLog
index 0552085..9fa2e7a 100644
--- a/Source/WebInspectorUI/ChangeLog
+++ b/Source/WebInspectorUI/ChangeLog
@@ -1,3 +1,80 @@
+2019-10-15 Devin Rousso <drousso@apple.com>
+
+ Web Inspector: Local Resource Overrides: automatically create an image/font local override when dragging content over a non-overridden resource
+ https://bugs.webkit.org/show_bug.cgi?id=202957
+
+ Reviewed by Joseph Pecoraro.
+
+ Since non-text resources aren't editable, some users of local resource overrides kept trying
+ to drag image/font files over the actual resource content view (not the local override),
+ which didn't do anything. Rather than having to click "Create Local Override" and then do a
+ drag/drop, we should support the "shorter" workflow of drag/drop over the actual resource.
+
+ * UserInterface/Base/FileUtilities.js:
+ (WI.FileUtilities.import): Added.
+ (WI.FileUtilities.importText):
+ (WI.FileUtilities.importJSON):
+ (WI.FileUtilities.importData): Added.
+ (WI.FileUtilities.async readText):
+ (WI.FileUtilities.async readJSON):
+ (WI.FileUtilities.async readData): Added.
+ (WI.FileUtilities.async _read): Added.
+ Create utility functions for importing non-text content as data.
+ Drive-by: fix a bug in the `import*` functions where the `callback` would be bound on the
+ first call, meaning that since the `<input>` was cached for all calls, we'd only
+ ever use the first `callback` in subsequent calls.
+
+ * UserInterface/Views/DropZoneView.js:
+ (WI.DropZoneView):
+ (WI.DropZoneView.prototype.set text): Added.
+ Support the text content of the drop zone changing after it's initialized.
+
+ * UserInterface/Views/FontResourceContentView.js:
+ (WI.FontResourceContentView):
+ (WI.FontResourceContentView.prototype.contentAvailable):
+ (WI.FontResourceContentView.prototype.dropZoneHandleDragEnter): Added.
+ (WI.FontResourceContentView.prototype.dropZoneHandleDrop):
+ (WI.FontResourceContentView.prototype._handleLocalResourceContentDidChange): Added.
+ * UserInterface/Views/ImageResourceContentView.js:
+ (WI.ImageResourceContentView):
+ (WI.ImageResourceContentView.prototype.contentAvailable):
+ (WI.ImageResourceContentView.prototype.dropZoneHandleDragEnter): Added.
+ (WI.ImageResourceContentView.prototype.dropZoneHandleDrop):
+ (WI.ImageResourceContentView.prototype._handleLocalResourceContentDidChange): Added.
+ Support drag/drop on non-override image/font content views, which will create/update the
+ local resource override for that resource.
+
+ * UserInterface/Views/ResourceContentView.js:
+ (WI.ResourceContentView):
+ (WI.ResourceContentView.prototype.get navigationItems):
+ (WI.ResourceContentView.prototype._handleImportLocalResourceOverride): Added.
+ When viewing a local resource override, add an "Import" navigation item for non-drag/drop
+ updating of the contents of the local resource override. This is also exposed for text-based
+ local resource overrides, since drag/drop inserts the contents of the file (if it's text),
+ which attempts to determine whether the dropped file is text or data based on the MIME type.
+
+ * UserInterface/Views/AuditNavigationSidebarPanel.js:
+ (WI.AuditNavigationSidebarPanel.prototype._handleImportButtonNavigationItemClicked):
+ * UserInterface/Views/CanvasOverviewContentView.js:
+ (WI.CanvasOverviewContentView.prototype._handleImportButtonNavigationItemClicked):
+ * UserInterface/Views/CanvasSidebarPanel.js:
+ (WI.CanvasSidebarPanel.prototype._handleImportButtonNavigationItemClicked):
+ * UserInterface/Views/HeapAllocationsTimelineView.js:
+ (WI.HeapAllocationsTimelineView.prototype._importButtonNavigationItemClicked):
+ * UserInterface/Views/NetworkTableContentView.js:
+ (WI.NetworkTableContentView.prototype._importHAR):
+ * UserInterface/Views/TimelineRecordingContentView.js:
+ (WI.TimelineRecordingContentView.prototype._importButtonNavigationItemClicked):
+ Explicitly allow multiple files to be imported at the same time.
+
+ * UserInterface/Base/BlobUtilities.js:
+ (WI.BlobUtilities.blobForContent):
+ Drive-by: remove extra `base64Encoded` argument when calling `decodeBase64ToBlob`, which
+ caused SVG-based image local resource overrides to show a broken image when
+ closing and reopening Web Inspector.
+
+ * Localizations/en.lproj/localizedStrings.js:
+
2019-10-14 Devin Rousso <drousso@apple.com>
Web Inspector: REGRESSION(r250991): Sources: local resource overrides should be enabled when not in tests
diff --git a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
index 78a889b..f19f154 100644
--- a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
+++ b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
@@ -382,8 +382,6 @@
localizedStrings["Done"] = "Done";
localizedStrings["Download"] = "Download";
localizedStrings["Download Web Archive"] = "Download Web Archive";
-localizedStrings["Drop Font"] = "Drop Font";
-localizedStrings["Drop Image"] = "Drop Image";
localizedStrings["Dropped Element"] = "Dropped Element";
localizedStrings["Dropped Node"] = "Dropped Node";
localizedStrings["Duplicate Selector"] = "Duplicate Selector";
@@ -1217,6 +1215,9 @@
localizedStrings["Unsupported property name"] = "Unsupported property name";
localizedStrings["Unsupported property value"] = "Unsupported property value";
localizedStrings["Untitled"] = "Untitled";
+localizedStrings["Update Font"] = "Update Font";
+localizedStrings["Update Image"] = "Update Image";
+localizedStrings["Update Local Override"] = "Update Local Override";
localizedStrings["Usage: %s"] = "Usage: %s";
localizedStrings["Use Default Appearance"] = "Use Default Appearance";
localizedStrings["Use Mock Capture Devices"] = "Use Mock Capture Devices";
diff --git a/Source/WebInspectorUI/UserInterface/Base/BlobUtilities.js b/Source/WebInspectorUI/UserInterface/Base/BlobUtilities.js
index 08c60b0..da6f055 100644
--- a/Source/WebInspectorUI/UserInterface/Base/BlobUtilities.js
+++ b/Source/WebInspectorUI/UserInterface/Base/BlobUtilities.js
@@ -27,7 +27,7 @@
static blobForContent(content, base64Encoded, mimeType)
{
if (base64Encoded)
- return BlobUtilities.decodeBase64ToBlob(content, base64Encoded, mimeType);
+ return BlobUtilities.decodeBase64ToBlob(content, mimeType);
return BlobUtilities.textToBlob(content, mimeType);
}
diff --git a/Source/WebInspectorUI/UserInterface/Base/FileUtilities.js b/Source/WebInspectorUI/UserInterface/Base/FileUtilities.js
index ff7d8d0..68a0275 100644
--- a/Source/WebInspectorUI/UserInterface/Base/FileUtilities.js
+++ b/Source/WebInspectorUI/UserInterface/Base/FileUtilities.js
@@ -91,38 +91,105 @@
fileReader.readAsDataURL(saveData.content);
}
- static importText(callback)
+ static import(callback, {multiple} = {})
{
- if (!FileUtilities._importTextInputElement) {
- let inputElement = FileUtilities._importTextInputElement = document.createElement("input");
- inputElement.type = "file";
- inputElement.multiple = true;
- inputElement.addEventListener("change", (event) => {
- WI.FileUtilities.readText(inputElement.files, callback);
- });
- }
+ let inputElement = document.createElement("input");
+ inputElement.type = "file";
+ inputElement.value = null;
+ inputElement.multiple = !!multiple;
+ inputElement.addEventListener("change", (event) => {
+ callback(inputElement.files);
+ });
- FileUtilities._importTextInputElement.value = null;
- FileUtilities._importTextInputElement.click();
+ inputElement.click();
+
+ // Cache the last used import element so that it doesn't get GCd while the native file
+ // picker is shown, which would prevent the "change" event listener from firing.
+ FileUtilities.importInputElement = inputElement;
}
- static importJSON(callback)
+ static importText(callback, options = {})
{
- if (!FileUtilities._importJSONInputElement) {
- let inputElement = FileUtilities._importJSONInputElement = document.createElement("input");
- inputElement.type = "file";
- inputElement.multiple = true;
- inputElement.addEventListener("change", (event) => {
- WI.FileUtilities.readJSON(inputElement.files, callback);
- });
- }
+ FileUtilities.import((files) => {
+ FileUtilities.readText(files, callback);
+ }, options);
+ }
- FileUtilities._importJSONInputElement.value = null;
- FileUtilities._importJSONInputElement.click();
+ static importJSON(callback, options = {})
+ {
+ FileUtilities.import((files) => {
+ FileUtilities.readJSON(files, callback);
+ }, options);
+ }
+
+ static importData(callback, options = {})
+ {
+ FileUtilities.import((files) => {
+ FileUtilities.readData(files, callback);
+ }, options);
}
static async readText(fileOrList, callback)
{
+ await FileUtilities._read(fileOrList, async (file, result) => {
+ await new Promise((resolve, reject) => {
+ let reader = new FileReader;
+ reader.addEventListener("loadend", (event) => {
+ result.text = reader.result;
+ resolve(event);
+ });
+ reader.addEventListener("error", reject);
+ reader.readAsText(file);
+ });
+ }, callback);
+ }
+
+ static async readJSON(fileOrList, callback)
+ {
+ await WI.FileUtilities.readText(fileOrList, async (result) => {
+ if (result.text && !result.error) {
+ try {
+ result.json = JSON.parse(result.text);
+ } catch (e) {
+ result.error = e;
+ }
+ }
+
+ await callback(result);
+ });
+ }
+
+ static async readData(fileOrList, callback)
+ {
+ await FileUtilities._read(fileOrList, async (file, result) => {
+ await new Promise((resolve, reject) => {
+ let reader = new FileReader;
+ reader.addEventListener("loadend", (event) => {
+ let {mimeType, base64, data} = parseDataURL(reader.result);
+
+ // In case no mime type was determined, try to derive one from the file extension.
+ if (!mimeType || mimeType === "text/plain") {
+ let extension = WI.fileExtensionForFilename(result.filename);
+ if (extension)
+ mimeType = WI.mimeTypeForFileExtension(extension);
+ }
+
+ result.mimeType = mimeType;
+ result.base64Encoded = base64;
+ result.content = data;
+
+ resolve(event);
+ });
+ reader.addEventListener("error", reject);
+ reader.readAsDataURL(file);
+ });
+ }, callback);
+ }
+
+ // Private
+
+ static async _read(fileOrList, operation, callback)
+ {
console.assert(fileOrList instanceof File || fileOrList instanceof FileList);
let files = [];
@@ -137,37 +204,12 @@
};
try {
- await new Promise((resolve, reject) => {
- let reader = new FileReader;
- reader.addEventListener("loadend", (event) => {
- result.text = reader.result;
- resolve(event);
- });
- reader.addEventListener("error", reject);
- reader.readAsText(file);
- });
+ await operation(file, result);
} catch (e) {
result.error = e;
}
- let promise = callback(result);
- if (promise instanceof Promise)
- await promise;
+ await callback(result);
}
}
-
- static async readJSON(fileOrList, callback)
- {
- return WI.FileUtilities.readText(fileOrList, (result) => {
- if (result.text && !result.error) {
- try {
- result.json = JSON.parse(result.text);
- } catch (e) {
- result.error = e;
- }
- }
-
- return callback(result);
- });
- }
};
diff --git a/Source/WebInspectorUI/UserInterface/Views/AuditNavigationSidebarPanel.js b/Source/WebInspectorUI/UserInterface/Views/AuditNavigationSidebarPanel.js
index b5c196d..65c9336 100644
--- a/Source/WebInspectorUI/UserInterface/Views/AuditNavigationSidebarPanel.js
+++ b/Source/WebInspectorUI/UserInterface/Views/AuditNavigationSidebarPanel.js
@@ -315,7 +315,7 @@
_handleImportButtonNavigationItemClicked(event)
{
- WI.FileUtilities.importJSON((result) => WI.auditManager.processJSON(result));
+ WI.FileUtilities.importJSON((result) => WI.auditManager.processJSON(result), {multiple: true});
}
_handleEditButtonNavigationItemClicked(event)
diff --git a/Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.js b/Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.js
index 964cc5f..959aa94 100644
--- a/Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.js
@@ -336,7 +336,7 @@
_handleImportButtonNavigationItemClicked(event)
{
- WI.FileUtilities.importJSON((result) => WI.canvasManager.processJSON(result));
+ WI.FileUtilities.importJSON((result) => WI.canvasManager.processJSON(result), {multiple: true});
}
_handleRecordingSaved(event)
diff --git a/Source/WebInspectorUI/UserInterface/Views/CanvasSidebarPanel.js b/Source/WebInspectorUI/UserInterface/Views/CanvasSidebarPanel.js
index 3159f03..c2bbdc0 100644
--- a/Source/WebInspectorUI/UserInterface/Views/CanvasSidebarPanel.js
+++ b/Source/WebInspectorUI/UserInterface/Views/CanvasSidebarPanel.js
@@ -322,7 +322,7 @@
_handleImportButtonNavigationItemClicked(event)
{
- WI.FileUtilities.importJSON((result) => WI.canvasManager.processJSON(result));
+ WI.FileUtilities.importJSON((result) => WI.canvasManager.processJSON(result), {multiple: true});
}
_treeSelectionDidChange(event)
diff --git a/Source/WebInspectorUI/UserInterface/Views/DropZoneView.js b/Source/WebInspectorUI/UserInterface/Views/DropZoneView.js
index ea35d7d..a772dd7 100644
--- a/Source/WebInspectorUI/UserInterface/Views/DropZoneView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/DropZoneView.js
@@ -30,7 +30,7 @@
WI.DropZoneView = class DropZoneView extends WI.View
{
- constructor(delegate, {text} = {})
+ constructor(delegate)
{
console.assert(delegate);
console.assert(typeof delegate.dropZoneShouldAppearForDragEvent === "function");
@@ -41,9 +41,6 @@
this._targetElement = null;
this._activelyHandlingDrag = false;
- if (text)
- this.element.textContent = text;
-
this.element.classList.add("drop-zone");
}
@@ -75,6 +72,11 @@
this._targetElement.addEventListener("dragenter", this._boundHandleDragEnter);
}
+ set text(text)
+ {
+ this.element.textContent = text;
+ }
+
// Protected
initialLayout()
diff --git a/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.js b/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.js
index 112d98f..8993e5e 100644
--- a/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.js
@@ -32,9 +32,6 @@
this._styleElement = null;
this._previewElement = null;
this._previewContainer = null;
-
- if (this.showingLocalResourceOverride)
- this._dropZoneView = new WI.DropZoneView(this, {text: WI.UIString("Drop Font")});
}
// Public
@@ -68,9 +65,13 @@
this._updatePreviewElement();
- if (this._dropZoneView) {
- this._dropZoneView.targetElement = this._previewContainer;
- this.addSubview(this._dropZoneView);
+ if (WI.NetworkManager.supportsLocalResourceOverrides()) {
+ let dropZoneView = new WI.DropZoneView(this);
+ dropZoneView.targetElement = this._previewContainer;
+ this.addSubview(dropZoneView);
+
+ if (this.showingLocalResourceOverride)
+ this.resource.addEventListener(WI.SourceCode.Event.ContentDidChange, this._handleLocalResourceContentDidChange, this);
}
}
@@ -118,38 +119,39 @@
return event.dataTransfer.types.includes("Files");
}
+ dropZoneHandleDragEnter(dropZone, event)
+ {
+ if (this.showingLocalResourceOverride)
+ dropZone.text = WI.UIString("Update Font");
+ else if (WI.networkManager.localResourceOverrideForURL(this.resource.url))
+ dropZone.text = WI.UIString("Update Local Override");
+ else
+ dropZone.text = WI.UIString("Create Local Override");
+ }
+
dropZoneHandleDrop(dropZone, event)
{
let files = event.dataTransfer.files;
- let file = files.length === 1 ? files[0] : null;
- if (!file) {
+ if (files.length !== 1) {
InspectorFrontendHost.beep();
return;
}
- let fileReader = new FileReader;
- fileReader.addEventListener("loadend", (event) => {
+ WI.FileUtilities.readData(files, async ({dataURL, mimeType, base64Encoded, content}) => {
let localResourceOverride = WI.networkManager.localResourceOverrideForURL(this.resource.url);
- if (!localResourceOverride)
- return;
-
- let dataURL = fileReader.result;
- let {base64, data, mimeType} = parseDataURL(dataURL);
-
- // In case no mime type was determined, try to derive one from the file extension.
- if (!mimeType || mimeType === "text/plain") {
- let extension = WI.fileExtensionForFilename(file.name);
- if (extension)
- mimeType = WI.mimeTypeForFileExtension(extension);
+ if (!localResourceOverride && !this.showingLocalResourceOverride) {
+ localResourceOverride = await this.resource.createLocalResourceOverride();
+ WI.networkManager.addLocalResourceOverride(localResourceOverride);
}
- let revision = localResourceOverride.localResource.currentRevision;
- revision.updateRevisionContent(data, {base64Encoded: base64, mimeType});
+ console.assert(localResourceOverride);
- this._fontObjectURL = this.resource.createObjectURL();
- this._updatePreviewElement();
+ let revision = localResourceOverride.localResource.currentRevision;
+ revision.updateRevisionContent(content, {base64Encoded, mimeType});
+
+ if (!this.showingLocalResourceOverride)
+ WI.showLocalResourceOverride(localResourceOverride);
});
- fileReader.readAsDataURL(file);
}
// Private
@@ -208,6 +210,12 @@
this.sizeToFit();
}
+
+ _handleLocalResourceContentDidChange(event)
+ {
+ this._fontObjectURL = this.resource.createObjectURL();
+ this._updatePreviewElement();
+ }
};
WI.FontResourceContentView._uniqueFontIdentifier = 0;
diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js b/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js
index d1f32dc..ec5145a 100644
--- a/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js
@@ -403,7 +403,7 @@
WI.timelineManager.heapSnapshotAdded(timestamp, snapshot);
this.dispatchEventToListeners(WI.TimelineView.Event.NeedsEntireSelectedRange);
});
- });
+ }, {multiple: true});
}
_takeHeapSnapshotClicked()
diff --git a/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.js b/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.js
index 58f499b..a302b17 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.js
@@ -40,9 +40,6 @@
this._showGridButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
this._showGridButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._showGridButtonClicked, this);
this._showGridButtonNavigationItem.activated = !!WI.settings.showImageGrid.value;
-
- if (this.showingLocalResourceOverride)
- this._dropZoneView = new WI.DropZoneView(this, {text: WI.UIString("Drop Image")});
}
// Public
@@ -89,9 +86,13 @@
this._draggingInternalImageElement = false;
});
- if (this._dropZoneView) {
- this._dropZoneView.targetElement = imageContainer;
- this.addSubview(this._dropZoneView);
+ if (WI.NetworkManager.supportsLocalResourceOverrides()) {
+ let dropZoneView = new WI.DropZoneView(this);
+ dropZoneView.targetElement = imageContainer;
+ this.addSubview(dropZoneView);
+
+ if (this.showingLocalResourceOverride)
+ this.resource.addEventListener(WI.SourceCode.Event.ContentDidChange, this._handleLocalResourceContentDidChange, this);
}
}
@@ -132,37 +133,39 @@
return event.dataTransfer.types.includes("Files");
}
+ dropZoneHandleDragEnter(dropZone, event)
+ {
+ if (this.showingLocalResourceOverride)
+ dropZone.text = WI.UIString("Update Image");
+ else if (WI.networkManager.localResourceOverrideForURL(this.resource.url))
+ dropZone.text = WI.UIString("Update Local Override");
+ else
+ dropZone.text = WI.UIString("Create Local Override");
+ }
+
dropZoneHandleDrop(dropZone, event)
{
let files = event.dataTransfer.files;
- let file = files.length === 1 ? files[0] : null;
- if (!file) {
+ if (files.length !== 1) {
InspectorFrontendHost.beep();
return;
}
- let fileReader = new FileReader;
- fileReader.addEventListener("loadend", (event) => {
+ WI.FileUtilities.readData(files, async ({dataURL, mimeType, base64Encoded, content}) => {
let localResourceOverride = WI.networkManager.localResourceOverrideForURL(this.resource.url);
- if (!localResourceOverride)
- return;
-
- let dataURL = fileReader.result;
- this._imageElement.src = dataURL;
-
- let {base64, data, mimeType} = parseDataURL(dataURL);
-
- // In case no mime type was determined, try to derive one from the file extension.
- if (!mimeType || mimeType === "text/plain") {
- let extension = WI.fileExtensionForFilename(file.name);
- if (extension)
- mimeType = WI.mimeTypeForFileExtension(extension);
+ if (!localResourceOverride && !this.showingLocalResourceOverride) {
+ localResourceOverride = await this.resource.createLocalResourceOverride();
+ WI.networkManager.addLocalResourceOverride(localResourceOverride);
}
+ console.assert(localResourceOverride);
+
let revision = localResourceOverride.localResource.currentRevision;
- revision.updateRevisionContent(data, {base64Encoded: base64, mimeType});
+ revision.updateRevisionContent(content, {base64Encoded, mimeType});
+
+ if (!this.showingLocalResourceOverride)
+ WI.showLocalResourceOverride(localResourceOverride);
});
- fileReader.readAsDataURL(file);
}
// Private
@@ -183,4 +186,9 @@
this._updateImageGrid();
}
+
+ _handleLocalResourceContentDidChange(event)
+ {
+ this._imageElement.src = this.resource.createObjectURL();
+ }
};
diff --git a/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js b/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js
index 2796713..b8d7a29 100644
--- a/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js
@@ -2150,7 +2150,7 @@
_importHAR()
{
- WI.FileUtilities.importJSON((result) => this.processHAR(result));
+ WI.FileUtilities.importJSON((result) => this.processHAR(result), {multiple: true});
}
_waterfallPopoverContent()
diff --git a/Source/WebInspectorUI/UserInterface/Views/ResourceContentView.js b/Source/WebInspectorUI/UserInterface/Views/ResourceContentView.js
index 82b361b..2e7b2ce 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ResourceContentView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ResourceContentView.js
@@ -66,9 +66,13 @@
this._localResourceOverrideBannerView = new WI.LocalResourceOverrideLabelView(resource);
+ this._importLocalResourceOverrideButtonNavigationItem = new WI.ButtonNavigationItem("import-local-resource-override", WI.UIString("Import"), "Images/Import.svg", 15, 15);
+ this._importLocalResourceOverrideButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
+ this._importLocalResourceOverrideButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
+ this._importLocalResourceOverrideButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportLocalResourceOverride, this);
+
this._removeLocalResourceOverrideButtonNavigationItem = new WI.ButtonNavigationItem("remove-local-resource-override", WI.UIString("Remove Local Override"), "Images/NavigationItemTrash.svg", 15, 15);
this._removeLocalResourceOverrideButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleRemoveLocalResourceOverride, this);
- this._removeLocalResourceOverrideButtonNavigationItem.enabled = true;
this._removeLocalResourceOverrideButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
} else {
this._localResourceOverrideBannerView = new WI.LocalResourceOverrideWarningView(resource);
@@ -93,6 +97,8 @@
{
let items = [];
+ if (this._importLocalResourceOverrideButtonNavigationItem)
+ items.push(this._importLocalResourceOverrideButtonNavigationItem, new WI.DividerNavigationItem);
if (this._removeLocalResourceOverrideButtonNavigationItem)
items.push(this._removeLocalResourceOverrideButtonNavigationItem);
if (this._createLocalResourceOverrideButtonNavigationItem)
@@ -232,6 +238,35 @@
WI.showLocalResourceOverride(localResourceOverride);
}
+ _handleImportLocalResourceOverride(event)
+ {
+ console.assert(this._showingLocalResourceOverride);
+
+ WI.FileUtilities.import(async (fileList) => {
+ console.assert(fileList.length === 1);
+
+ let localResourceOverride = WI.networkManager.localResourceOverrideForURL(this.resource.url);
+ console.assert(localResourceOverride);
+
+ let revision = localResourceOverride.localResource.currentRevision;
+
+ let file = fileList[0];
+ let mimeType = file.type || WI.mimeTypeForFileExtension(WI.fileExtensionForFilename(file.name));
+ if (WI.shouldTreatMIMETypeAsText(mimeType)) {
+ await WI.FileUtilities.readText(file, ({text}) => {
+ revision.updateRevisionContent(text, {base64Encoded: false, mimeType});
+ });
+ } else {
+ await WI.FileUtilities.readData(file, ({dataURL, mimeType, base64Encoded, content}) => {
+ revision.updateRevisionContent(content, {base64Encoded, mimeType});
+ });
+ }
+
+ if (!this.showingLocalResourceOverride)
+ WI.showLocalResourceOverride(localResourceOverride);
+ });
+ }
+
_handleRemoveLocalResourceOverride(event)
{
console.assert(this._showingLocalResourceOverride);
diff --git a/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js b/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js
index 7c76711..1cecfff 100644
--- a/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js
@@ -621,7 +621,7 @@
_importButtonNavigationItemClicked(event)
{
- WI.FileUtilities.importJSON((result) => WI.timelineManager.processJSON(result));
+ WI.FileUtilities.importJSON((result) => WI.timelineManager.processJSON(result), {multiple: true});
}
_clearTimeline(event)