Web Inspector: Local Resource Overrides: UI for overriding image and font resource content
https://bugs.webkit.org/show_bug.cgi?id=202016
<rdar://problem/55541475>
Reviewed by Devin Rousso.
Source/WebInspectorUI:
Extend SourceCodeRevision to be a (content, base64Encoded, mimeType) tuple and
make clients update the revision content more explicitly (`updateRevisionContent`).
This also includes `blobContent` as a more explicit way to get the content as
a Blob, which may not always be desired.
Switch LocalResource use the originalRevision / currentRevision instead of
keeping its own localContent / localContentIsBase64Encoded properties.
Introduce a `DropZoneView` to simplify handling of presenting a drop zone
over a specific element. And use it for the ImageResourceContentView for local
resource overrides to accept new content.
* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Main.html:
New strings and resources.
* .eslintrc:
* UserInterface/Base/BlobUtilities.js: Added.
(WI.BlobUtilities.blobForContent):
(WI.BlobUtilities.decodeBase64ToBlob):
(WI.BlobUtilities.textToBlob):
(WI.BlobUtilities.blobAsText):
(WI.BlobUtilities):
* UserInterface/Base/FileUtilities.js:
(WI.FileUtilities.async.readDataURL):
(WI.FileUtilities):
* UserInterface/Base/MIMETypeUtilities.js:
(WI.fileExtensionForFilename):
(WI.fileExtensionForURL):
* UserInterface/Base/Utilities.js:
Move around or introduce some minor utilities.
* UserInterface/Models/SourceCodeRevision.js:
(WI.SourceCodeRevision):
(WI.SourceCodeRevision.prototype.get sourceCode):
(WI.SourceCodeRevision.prototype.get content):
(WI.SourceCodeRevision.prototype.get base64Encoded):
(WI.SourceCodeRevision.prototype.get mimeType):
(WI.SourceCodeRevision.prototype.get blobContent):
(WI.SourceCodeRevision.prototype.updateRevisionContent):
(WI.SourceCodeRevision.prototype.copy):
(WI.SourceCodeRevision.prototype.set content): Deleted.
Data is now a (content, base64Encoded, mimeType) tuple.
* UserInterface/Controllers/NetworkManager.js:
(WI.NetworkManager.prototype.responseIntercepted):
(WI.NetworkManager.prototype._handleResourceContentDidChange):
(WI.NetworkManager.prototype._persistLocalResourceOverrideSoonAfterContentChange): Deleted.
This is now a unified resource content change path without anything special for
local resource overrides.
* UserInterface/Models/LocalResource.js:
(WI.LocalResource.prototype.toJSON):
(WI.LocalResource.prototype.requestContentFromBackend):
(WI.LocalResource.prototype.handleCurrentRevisionContentChange):
(WI.LocalResource):
(WI.LocalResource.prototype.get localContent): Deleted.
(WI.LocalResource.prototype.get localContentIsBase64Encoded): Deleted.
(WI.LocalResource.prototype.hasContent): Deleted.
(WI.LocalResource.prototype.setContent): Deleted.
(WI.LocalResource.prototype.updateOverrideContent): Deleted.
Use originalRevision / currentRevision as appropriate.
* UserInterface/Views/DropZoneView.css: Added.
(.drop-zone):
(.drop-zone.visible):
(@media (prefers-color-scheme: dark)):
* UserInterface/Views/DropZoneView.js: Added.
(WI.DropZoneView):
(WI.DropZoneView.prototype.get delegate):
(WI.DropZoneView.prototype.get targetElement):
(WI.DropZoneView.prototype.set targetElement):
(WI.DropZoneView.prototype.initialLayout):
(WI.DropZoneView.prototype._startActiveDrag):
(WI.DropZoneView.prototype._stopActiveDrag):
(WI.DropZoneView.prototype._handleDragEnter):
(WI.DropZoneView.prototype._handleDragLeave):
(WI.DropZoneView.prototype._handleDragOver):
(WI.DropZoneView.prototype._handleDrop):
Simplified handling of a drop zone.
* UserInterface/Views/ResourceContentView.js:
(WI.ResourceContentView.prototype.removeLoadingIndicator):
More safely remove children and subviews.
(WI.ResourceContentView):
(WI.ResourceContentView.prototype.get resource):
(WI.ResourceContentView.prototype.get navigationItems):
(WI.ResourceContentView.prototype.localResourceOverrideInitialContent):
(WI.ResourceContentView.prototype.closed):
(WI.ResourceContentView.prototype.removeLoadingIndicator):
(WI.ResourceContentView.prototype._contentAvailable):
(WI.ResourceContentView.prototype._issueWasAdded):
(WI.ResourceContentView.prototype.async._handleCreateLocalResourceOverride):
(WI.ResourceContentView.prototype._handleRemoveLocalResourceOverride):
(WI.ResourceContentView.prototype._handleLocalResourceOverrideChanged):
(WI.ResourceContentView.prototype._mouseWasClicked):
* UserInterface/Views/TextResourceContentView.js:
(WI.TextResourceContentView):
(WI.TextResourceContentView.prototype.get navigationItems):
(WI.TextResourceContentView.prototype.localResourceOverrideInitialContent):
(WI.TextResourceContentView.prototype._contentWillPopulate):
(WI.TextResourceContentView.prototype._contentDidPopulate):
(WI.TextResourceContentView.prototype._textEditorContentDidChange):
(WI.TextResourceContentView.prototype._shouldBeEditable):
(WI.TextResourceContentView.prototype.async._handleCreateLocalResourceOverride): Deleted.
(WI.TextResourceContentView.prototype._handleRemoveLocalResourceOverride): Deleted.
(WI.TextResourceContentView.prototype._handleLocalResourceOverrideChanged): Deleted.
Extract generalized local resource override properties into the ResourceContentView base class.
* UserInterface/Views/FontResourceContentView.css:
(.content-view.resource.font):
(.content-view.resource.font > .drop-zone):
(.content-view.resource.font > .preview-container):
(.content-view.resource.font .preview):
* UserInterface/Views/FontResourceContentView.js:
(WI.FontResourceContentView):
(WI.FontResourceContentView.prototype.contentAvailable):
(WI.FontResourceContentView.prototype.shown):
(WI.FontResourceContentView.prototype.hidden):
(WI.FontResourceContentView.prototype.closed):
(WI.FontResourceContentView.prototype.layout):
(WI.FontResourceContentView.prototype._updatePreviewElement.createMetricElement):
(WI.FontResourceContentView.prototype._updatePreviewElement):
(WI.FontResourceContentView.prototype.dropZoneShouldAppearForDragEvent):
(WI.FontResourceContentView.prototype.dropZoneHandleDrop):
Create a drop zone that will update the font local resource override content.
* UserInterface/Views/ImageResourceContentView.css:
(.content-view.resource.image):
(.content-view.resource.image > .drop-zone):
(.content-view.resource.image > .img-container):
(.content-view.resource.image img):
* UserInterface/Views/ImageResourceContentView.js:
(WI.ImageResourceContentView):
(WI.ImageResourceContentView.prototype.get navigationItems):
(WI.ImageResourceContentView.prototype.contentAvailable):
(WI.ImageResourceContentView.prototype.closed):
(WI.ImageResourceContentView.prototype.dropZoneShouldAppearForDragEvent):
(WI.ImageResourceContentView.prototype.dropZoneHandleDrop):
Create a drop zone that will update the image local resource override content.
* UserInterface/Models/Script.js:
(WI.Script.prototype.get mimeType):
Seems like this should have a default value given there may not be a resource.
* UserInterface/Views/LocalResourceOverridePopover.js:
(WI.LocalResourceOverridePopover.prototype.show):
Better handling here, since the utilities expects a number not a string.
* UserInterface/Models/Resource.js:
(WI.Resource.prototype.createObjectURL):
* UserInterface/Views/LocalResourceOverrideTreeElement.js:
(WI.LocalResourceOverrideTreeElement.prototype.willDismissPopover):
Use currentRevision more appropriately.
* UserInterface/Models/SourceCode.js:
(WI.SourceCode.prototype._processContent):
* UserInterface/Views/TextResourceContentView.js:
(WI.TextResourceContentView.prototype._textEditorContentDidChange):
* UserInterface/Controllers/CSSManager.js:
(WI.CSSManager.prototype._resourceContentDidChange.applyStyleSheetChanges.styleSheetFound):
(WI.CSSManager.prototype._resourceContentDidChange.applyStyleSheetChanges):
(WI.CSSManager.prototype._resourceContentDidChange):
(WI.CSSManager.prototype._updateResourceContent.fetchedStyleSheetContent):
Update revision content more explicitly.
LayoutTests:
* inspector/unit-tests/mimetype-utilities-expected.txt:
* inspector/unit-tests/mimetype-utilities.html:
Test new utilities.
* http/tests/inspector/network/fetch-response-body.html:
* http/tests/inspector/network/xhr-response-body.html:
Renamed utilities.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251024 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index b0f0594..5c15fc1 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,19 @@
+2019-10-10 Joseph Pecoraro <pecoraro@apple.com>
+
+ Web Inspector: Local Resource Overrides: UI for overriding image and font resource content
+ https://bugs.webkit.org/show_bug.cgi?id=202016
+ <rdar://problem/55541475>
+
+ Reviewed by Devin Rousso.
+
+ * inspector/unit-tests/mimetype-utilities-expected.txt:
+ * inspector/unit-tests/mimetype-utilities.html:
+ Test new utilities.
+
+ * http/tests/inspector/network/fetch-response-body.html:
+ * http/tests/inspector/network/xhr-response-body.html:
+ Renamed utilities.
+
2019-10-11 Dean Jackson <dino@apple.com>
Layout test fast/events/touch/ios/tap-with-active-listener-inside-window-with-passive-listener.html is a flaky failure
diff --git a/LayoutTests/http/tests/inspector/network/fetch-response-body.html b/LayoutTests/http/tests/inspector/network/fetch-response-body.html
index c59c6a3..fdfc593 100644
--- a/LayoutTests/http/tests/inspector/network/fetch-response-body.html
+++ b/LayoutTests/http/tests/inspector/network/fetch-response-body.html
@@ -42,7 +42,7 @@
resolve(content)
return;
}
- blobAsText(content, (text) => {
+ WI.BlobUtilities.blobAsText(content, (text) => {
resolve(text);
});
});
diff --git a/LayoutTests/http/tests/inspector/network/xhr-response-body.html b/LayoutTests/http/tests/inspector/network/xhr-response-body.html
index dd49e832..99dbd0e 100644
--- a/LayoutTests/http/tests/inspector/network/xhr-response-body.html
+++ b/LayoutTests/http/tests/inspector/network/xhr-response-body.html
@@ -56,7 +56,7 @@
resolve(content)
return;
}
- blobAsText(content, (text) => {
+ WI.BlobUtilities.blobAsText(content, (text) => {
resolve(text);
});
});
diff --git a/LayoutTests/inspector/unit-tests/mimetype-utilities-expected.txt b/LayoutTests/inspector/unit-tests/mimetype-utilities-expected.txt
index 4fcd142..905cfc3 100644
--- a/LayoutTests/inspector/unit-tests/mimetype-utilities-expected.txt
+++ b/LayoutTests/inspector/unit-tests/mimetype-utilities-expected.txt
@@ -1,5 +1,15 @@
== Running test suite: MIMETypeUtilities
+-- Running test case: fileExtensionForFilename
+PASS: File extension for null filename should be null.
+PASS: File extension for filename without a period should be null.
+PASS: File extension for filename ending in a period should be null.
+PASS: File extension for "foo.xyz" should be "xyz".
+PASS: File extension for "image.png" should be "png".
+PASS: File extension for "image.png" should be "gif".
+PASS: File extension for "script.js" should be "js".
+PASS: File extension for "script.min.js" should be "js".
+
-- Running test case: fileExtensionForURL
PASS: File extension for null URL should be null.
PASS: File extension for invalid URL should be null.
diff --git a/LayoutTests/inspector/unit-tests/mimetype-utilities.html b/LayoutTests/inspector/unit-tests/mimetype-utilities.html
index 78e43c1..c181029 100644
--- a/LayoutTests/inspector/unit-tests/mimetype-utilities.html
+++ b/LayoutTests/inspector/unit-tests/mimetype-utilities.html
@@ -8,9 +8,26 @@
let suite = InspectorTest.createSyncSuite("MIMETypeUtilities");
suite.addTestCase({
+ name: "fileExtensionForFilename",
+ test() {
+ InspectorTest.expectNull(WI.fileExtensionForFilename(null), `File extension for null filename should be null.`);
+ InspectorTest.expectEqual(WI.fileExtensionForURL("test"), null, `File extension for filename without a period should be null.`);
+ InspectorTest.expectEqual(WI.fileExtensionForURL("test."), null, `File extension for filename ending in a period should be null.`);
+
+ InspectorTest.expectEqual(WI.fileExtensionForFilename("foo.xyz"), "xyz", `File extension for "foo.xyz" should be "xyz".`);
+ InspectorTest.expectEqual(WI.fileExtensionForFilename("image.png"), "png", `File extension for "image.png" should be "png".`);
+ InspectorTest.expectEqual(WI.fileExtensionForFilename("image.gif"), "gif", `File extension for "image.png" should be "gif".`);
+ InspectorTest.expectEqual(WI.fileExtensionForFilename("script.js"), "js", `File extension for "script.js" should be "js".`);
+ InspectorTest.expectEqual(WI.fileExtensionForFilename("script.min.js"), "js", `File extension for "script.min.js" should be "js".`);
+
+ return true;
+ }
+ });
+
+ suite.addTestCase({
name: "fileExtensionForURL",
test() {
- InspectorTest.expectEqual(WI.fileExtensionForURL(null), null, `File extension for null URL should be null.`);
+ InspectorTest.expectNull(WI.fileExtensionForURL(null), `File extension for null URL should be null.`);
InspectorTest.expectEqual(WI.fileExtensionForURL("invalid-url"), null, `File extension for invalid URL should be null.`);
InspectorTest.expectEqual(WI.fileExtensionForURL("https://example.com"), null, `File extension for URL without last path component should be null.`);
InspectorTest.expectEqual(WI.fileExtensionForURL("https://example.com/"), null, `File extension for URL without last path component should be null.`);
diff --git a/Source/WebInspectorUI/.eslintrc b/Source/WebInspectorUI/.eslintrc
index 50a2aab..d485ebf 100644
--- a/Source/WebInspectorUI/.eslintrc
+++ b/Source/WebInspectorUI/.eslintrc
@@ -111,7 +111,6 @@
// Utilities
"appendWebInspectorConsoleEvaluationSourceURL": true,
"appendWebInspectorSourceURL": true,
- "blobAsText": true,
"clamp": true,
"doubleQuotedString": true,
"ellipsis": true,
@@ -132,7 +131,6 @@
"parseMIMEType": true,
"resolveDotsInPath": true,
"simpleGlobStringToRegExp": true,
- "textToBlob": true,
"timestamp": true,
"zeroWidthSpace": true,
@@ -141,7 +139,6 @@
// URL Utilities
"absoluteURL": true,
- "decodeBase64ToBlob": true,
"parseDataURL": true,
"parseLocationQueryParameters": true,
"parseQueryString": true,
diff --git a/Source/WebInspectorUI/ChangeLog b/Source/WebInspectorUI/ChangeLog
index d6a8c2d..441c23d 100644
--- a/Source/WebInspectorUI/ChangeLog
+++ b/Source/WebInspectorUI/ChangeLog
@@ -1,3 +1,178 @@
+2019-10-10 Joseph Pecoraro <pecoraro@apple.com>
+
+ Web Inspector: Local Resource Overrides: UI for overriding image and font resource content
+ https://bugs.webkit.org/show_bug.cgi?id=202016
+ <rdar://problem/55541475>
+
+ Reviewed by Devin Rousso.
+
+ Extend SourceCodeRevision to be a (content, base64Encoded, mimeType) tuple and
+ make clients update the revision content more explicitly (`updateRevisionContent`).
+ This also includes `blobContent` as a more explicit way to get the content as
+ a Blob, which may not always be desired.
+
+ Switch LocalResource use the originalRevision / currentRevision instead of
+ keeping its own localContent / localContentIsBase64Encoded properties.
+
+ Introduce a `DropZoneView` to simplify handling of presenting a drop zone
+ over a specific element. And use it for the ImageResourceContentView for local
+ resource overrides to accept new content.
+
+ * Localizations/en.lproj/localizedStrings.js:
+ * UserInterface/Main.html:
+ New strings and resources.
+
+ * .eslintrc:
+ * UserInterface/Base/BlobUtilities.js: Added.
+ (WI.BlobUtilities.blobForContent):
+ (WI.BlobUtilities.decodeBase64ToBlob):
+ (WI.BlobUtilities.textToBlob):
+ (WI.BlobUtilities.blobAsText):
+ (WI.BlobUtilities):
+ * UserInterface/Base/FileUtilities.js:
+ (WI.FileUtilities.async.readDataURL):
+ (WI.FileUtilities):
+ * UserInterface/Base/MIMETypeUtilities.js:
+ (WI.fileExtensionForFilename):
+ (WI.fileExtensionForURL):
+ * UserInterface/Base/Utilities.js:
+ Move around or introduce some minor utilities.
+
+ * UserInterface/Models/SourceCodeRevision.js:
+ (WI.SourceCodeRevision):
+ (WI.SourceCodeRevision.prototype.get sourceCode):
+ (WI.SourceCodeRevision.prototype.get content):
+ (WI.SourceCodeRevision.prototype.get base64Encoded):
+ (WI.SourceCodeRevision.prototype.get mimeType):
+ (WI.SourceCodeRevision.prototype.get blobContent):
+ (WI.SourceCodeRevision.prototype.updateRevisionContent):
+ (WI.SourceCodeRevision.prototype.copy):
+ (WI.SourceCodeRevision.prototype.set content): Deleted.
+ Data is now a (content, base64Encoded, mimeType) tuple.
+
+ * UserInterface/Controllers/NetworkManager.js:
+ (WI.NetworkManager.prototype.responseIntercepted):
+ (WI.NetworkManager.prototype._handleResourceContentDidChange):
+ (WI.NetworkManager.prototype._persistLocalResourceOverrideSoonAfterContentChange): Deleted.
+ This is now a unified resource content change path without anything special for
+ local resource overrides.
+
+ * UserInterface/Models/LocalResource.js:
+ (WI.LocalResource.prototype.toJSON):
+ (WI.LocalResource.prototype.requestContentFromBackend):
+ (WI.LocalResource.prototype.handleCurrentRevisionContentChange):
+ (WI.LocalResource):
+ (WI.LocalResource.prototype.get localContent): Deleted.
+ (WI.LocalResource.prototype.get localContentIsBase64Encoded): Deleted.
+ (WI.LocalResource.prototype.hasContent): Deleted.
+ (WI.LocalResource.prototype.setContent): Deleted.
+ (WI.LocalResource.prototype.updateOverrideContent): Deleted.
+ Use originalRevision / currentRevision as appropriate.
+
+ * UserInterface/Views/DropZoneView.css: Added.
+ (.drop-zone):
+ (.drop-zone.visible):
+ (@media (prefers-color-scheme: dark)):
+ * UserInterface/Views/DropZoneView.js: Added.
+ (WI.DropZoneView):
+ (WI.DropZoneView.prototype.get delegate):
+ (WI.DropZoneView.prototype.get targetElement):
+ (WI.DropZoneView.prototype.set targetElement):
+ (WI.DropZoneView.prototype.initialLayout):
+ (WI.DropZoneView.prototype._startActiveDrag):
+ (WI.DropZoneView.prototype._stopActiveDrag):
+ (WI.DropZoneView.prototype._handleDragEnter):
+ (WI.DropZoneView.prototype._handleDragLeave):
+ (WI.DropZoneView.prototype._handleDragOver):
+ (WI.DropZoneView.prototype._handleDrop):
+ Simplified handling of a drop zone.
+
+ * UserInterface/Views/ResourceContentView.js:
+ (WI.ResourceContentView.prototype.removeLoadingIndicator):
+ More safely remove children and subviews.
+
+ (WI.ResourceContentView):
+ (WI.ResourceContentView.prototype.get resource):
+ (WI.ResourceContentView.prototype.get navigationItems):
+ (WI.ResourceContentView.prototype.localResourceOverrideInitialContent):
+ (WI.ResourceContentView.prototype.closed):
+ (WI.ResourceContentView.prototype.removeLoadingIndicator):
+ (WI.ResourceContentView.prototype._contentAvailable):
+ (WI.ResourceContentView.prototype._issueWasAdded):
+ (WI.ResourceContentView.prototype.async._handleCreateLocalResourceOverride):
+ (WI.ResourceContentView.prototype._handleRemoveLocalResourceOverride):
+ (WI.ResourceContentView.prototype._handleLocalResourceOverrideChanged):
+ (WI.ResourceContentView.prototype._mouseWasClicked):
+ * UserInterface/Views/TextResourceContentView.js:
+ (WI.TextResourceContentView):
+ (WI.TextResourceContentView.prototype.get navigationItems):
+ (WI.TextResourceContentView.prototype.localResourceOverrideInitialContent):
+ (WI.TextResourceContentView.prototype._contentWillPopulate):
+ (WI.TextResourceContentView.prototype._contentDidPopulate):
+ (WI.TextResourceContentView.prototype._textEditorContentDidChange):
+ (WI.TextResourceContentView.prototype._shouldBeEditable):
+ (WI.TextResourceContentView.prototype.async._handleCreateLocalResourceOverride): Deleted.
+ (WI.TextResourceContentView.prototype._handleRemoveLocalResourceOverride): Deleted.
+ (WI.TextResourceContentView.prototype._handleLocalResourceOverrideChanged): Deleted.
+ Extract generalized local resource override properties into the ResourceContentView base class.
+
+ * UserInterface/Views/FontResourceContentView.css:
+ (.content-view.resource.font):
+ (.content-view.resource.font > .drop-zone):
+ (.content-view.resource.font > .preview-container):
+ (.content-view.resource.font .preview):
+ * UserInterface/Views/FontResourceContentView.js:
+ (WI.FontResourceContentView):
+ (WI.FontResourceContentView.prototype.contentAvailable):
+ (WI.FontResourceContentView.prototype.shown):
+ (WI.FontResourceContentView.prototype.hidden):
+ (WI.FontResourceContentView.prototype.closed):
+ (WI.FontResourceContentView.prototype.layout):
+ (WI.FontResourceContentView.prototype._updatePreviewElement.createMetricElement):
+ (WI.FontResourceContentView.prototype._updatePreviewElement):
+ (WI.FontResourceContentView.prototype.dropZoneShouldAppearForDragEvent):
+ (WI.FontResourceContentView.prototype.dropZoneHandleDrop):
+ Create a drop zone that will update the font local resource override content.
+
+ * UserInterface/Views/ImageResourceContentView.css:
+ (.content-view.resource.image):
+ (.content-view.resource.image > .drop-zone):
+ (.content-view.resource.image > .img-container):
+ (.content-view.resource.image img):
+ * UserInterface/Views/ImageResourceContentView.js:
+ (WI.ImageResourceContentView):
+ (WI.ImageResourceContentView.prototype.get navigationItems):
+ (WI.ImageResourceContentView.prototype.contentAvailable):
+ (WI.ImageResourceContentView.prototype.closed):
+ (WI.ImageResourceContentView.prototype.dropZoneShouldAppearForDragEvent):
+ (WI.ImageResourceContentView.prototype.dropZoneHandleDrop):
+ Create a drop zone that will update the image local resource override content.
+
+ * UserInterface/Models/Script.js:
+ (WI.Script.prototype.get mimeType):
+ Seems like this should have a default value given there may not be a resource.
+
+ * UserInterface/Views/LocalResourceOverridePopover.js:
+ (WI.LocalResourceOverridePopover.prototype.show):
+ Better handling here, since the utilities expects a number not a string.
+
+ * UserInterface/Models/Resource.js:
+ (WI.Resource.prototype.createObjectURL):
+ * UserInterface/Views/LocalResourceOverrideTreeElement.js:
+ (WI.LocalResourceOverrideTreeElement.prototype.willDismissPopover):
+ Use currentRevision more appropriately.
+
+ * UserInterface/Models/SourceCode.js:
+ (WI.SourceCode.prototype._processContent):
+ * UserInterface/Views/TextResourceContentView.js:
+ (WI.TextResourceContentView.prototype._textEditorContentDidChange):
+ * UserInterface/Controllers/CSSManager.js:
+ (WI.CSSManager.prototype._resourceContentDidChange.applyStyleSheetChanges.styleSheetFound):
+ (WI.CSSManager.prototype._resourceContentDidChange.applyStyleSheetChanges):
+ (WI.CSSManager.prototype._resourceContentDidChange):
+ (WI.CSSManager.prototype._updateResourceContent.fetchedStyleSheetContent):
+ Update revision content more explicitly.
+
2019-10-10 Devin Rousso <drousso@apple.com>
Web Inspector: Sources: enable tab by default
diff --git a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
index 9e102f1..b6ae45f 100644
--- a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
+++ b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
@@ -379,6 +379,8 @@
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";
diff --git a/Source/WebInspectorUI/UserInterface/Base/BlobUtilities.js b/Source/WebInspectorUI/UserInterface/Base/BlobUtilities.js
new file mode 100644
index 0000000..08c60b0
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Base/BlobUtilities.js
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WI.BlobUtilities = class BlobUtilities {
+ static blobForContent(content, base64Encoded, mimeType)
+ {
+ if (base64Encoded)
+ return BlobUtilities.decodeBase64ToBlob(content, base64Encoded, mimeType);
+ return BlobUtilities.textToBlob(content, mimeType);
+ }
+
+ static decodeBase64ToBlob(base64Data, mimeType)
+ {
+ mimeType = mimeType || "";
+
+ const sliceSize = 1024;
+ let byteCharacters = atob(base64Data);
+ let bytesLength = byteCharacters.length;
+ let slicesCount = Math.ceil(bytesLength / sliceSize);
+ let byteArrays = new Array(slicesCount);
+
+ for (let sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
+ let begin = sliceIndex * sliceSize;
+ let end = Math.min(begin + sliceSize, bytesLength);
+
+ let bytes = new Array(end - begin);
+ for (let offset = begin, i = 0; offset < end; ++i, ++offset)
+ bytes[i] = byteCharacters[offset].charCodeAt(0);
+
+ byteArrays[sliceIndex] = new Uint8Array(bytes);
+ }
+
+ return new Blob(byteArrays, {type: mimeType});
+ }
+
+ static textToBlob(text, mimeType)
+ {
+ return new Blob([text], {type: mimeType});
+ }
+
+ static blobAsText(blob, callback)
+ {
+ console.assert(blob instanceof Blob);
+ let fileReader = new FileReader;
+ fileReader.addEventListener("loadend", () => { callback(fileReader.result); });
+ fileReader.readAsText(blob);
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Base/MIMETypeUtilities.js b/Source/WebInspectorUI/UserInterface/Base/MIMETypeUtilities.js
index 37ef947..698838d 100644
--- a/Source/WebInspectorUI/UserInterface/Base/MIMETypeUtilities.js
+++ b/Source/WebInspectorUI/UserInterface/Base/MIMETypeUtilities.js
@@ -23,20 +23,25 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-WI.fileExtensionForURL = function(url)
+WI.fileExtensionForFilename = function(filename)
{
- let lastPathComponent = parseURL(url).lastPathComponent;
- if (!lastPathComponent)
+ if (!filename)
return null;
- let index = lastPathComponent.lastIndexOf(".");
+ let index = filename.lastIndexOf(".");
if (index === -1)
return null;
- if (index === lastPathComponent.length - 1)
+ if (index === filename.length - 1)
return null;
- return lastPathComponent.substr(index + 1);
+ return filename.substr(index + 1);
+};
+
+WI.fileExtensionForURL = function(url)
+{
+ let lastPathComponent = parseURL(url).lastPathComponent;
+ return WI.fileExtensionForFilename(lastPathComponent);
};
WI.mimeTypeForFileExtension = function(extension)
diff --git a/Source/WebInspectorUI/UserInterface/Base/Utilities.js b/Source/WebInspectorUI/UserInterface/Base/Utilities.js
index c3e139e..4d8ac05 100644
--- a/Source/WebInspectorUI/UserInterface/Base/Utilities.js
+++ b/Source/WebInspectorUI/UserInterface/Base/Utilities.js
@@ -1674,40 +1674,3 @@
{
array.splice(insertionIndexForObjectInListSortedByFunction(object, array, comparator), 0, object);
}
-
-function decodeBase64ToBlob(base64Data, mimeType)
-{
- mimeType = mimeType || "";
-
- const sliceSize = 1024;
- var byteCharacters = atob(base64Data);
- var bytesLength = byteCharacters.length;
- var slicesCount = Math.ceil(bytesLength / sliceSize);
- var byteArrays = new Array(slicesCount);
-
- for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
- var begin = sliceIndex * sliceSize;
- var end = Math.min(begin + sliceSize, bytesLength);
-
- var bytes = new Array(end - begin);
- for (var offset = begin, i = 0; offset < end; ++i, ++offset)
- bytes[i] = byteCharacters[offset].charCodeAt(0);
-
- byteArrays[sliceIndex] = new Uint8Array(bytes);
- }
-
- return new Blob(byteArrays, {type: mimeType});
-}
-
-function textToBlob(text, mimeType)
-{
- return new Blob([text], {type: mimeType});
-}
-
-function blobAsText(blob, callback)
-{
- console.assert(blob instanceof Blob);
- let fileReader = new FileReader;
- fileReader.addEventListener("loadend", () => { callback(fileReader.result); });
- fileReader.readAsText(blob);
-}
diff --git a/Source/WebInspectorUI/UserInterface/Controllers/CSSManager.js b/Source/WebInspectorUI/UserInterface/Controllers/CSSManager.js
index 9c78ee4..0f8f20a 100644
--- a/Source/WebInspectorUI/UserInterface/Controllers/CSSManager.js
+++ b/Source/WebInspectorUI/UserInterface/Controllers/CSSManager.js
@@ -656,7 +656,7 @@
resource.__ignoreNextUpdateResourceContent = true;
let revision = styleSheet.currentRevision;
- revision.content = resource.content;
+ revision.updateRevisionContent(resource.content);
}
this._lookupStyleSheetForResource(resource, styleSheetFound.bind(this));
@@ -701,10 +701,10 @@
let revision = representedObject.currentRevision;
if (styleSheet.isInspectorStyleSheet()) {
- revision.content = representedObject.content;
+ revision.updateRevisionContent(representedObject.content);
styleSheet.dispatchEventToListeners(WI.SourceCode.Event.ContentDidChange);
} else
- revision.content = parameters.content;
+ revision.updateRevisionContent(parameters.content);
this._ignoreResourceContentDidChangeEventForResource = null;
}
diff --git a/Source/WebInspectorUI/UserInterface/Controllers/NetworkManager.js b/Source/WebInspectorUI/UserInterface/Controllers/NetworkManager.js
index fa4f33c..1b536b4 100644
--- a/Source/WebInspectorUI/UserInterface/Controllers/NetworkManager.js
+++ b/Source/WebInspectorUI/UserInterface/Controllers/NetworkManager.js
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -816,9 +816,15 @@
}
let localResource = localResourceOverride.localResource;
- let content = localResource.localContent;
- let base64Encoded = localResource.localContentIsBase64Encoded;
- let {mimeType, statusCode, statusText, responseHeaders} = localResource;
+ let revision = localResource.currentRevision;
+
+ let content = revision.content;
+ let base64Encoded = revision.base64Encoded;
+ let mimeType = revision.mimeType;
+ let statusCode = localResource.statusCode;
+ let statusText = localResource.statusText;
+ let responseHeaders = localResource.responseHeaders;
+ console.assert(revision.mimeType === localResource.mimeType);
if (isNaN(statusCode))
statusCode = undefined;
@@ -1244,17 +1250,6 @@
if (!localResourceOverride)
return;
- let localResource = localResourceOverride.localResource;
- let content = localResource.content;
- let base64Encoded = localResource.localContentIsBase64Encoded;
- let mimeType = localResource.mimeType;
- localResource.updateOverrideContent(content, base64Encoded, mimeType, {suppressEvent: true});
-
- this._persistLocalResourceOverrideSoonAfterContentChange(localResourceOverride);
- }
-
- _persistLocalResourceOverrideSoonAfterContentChange(localResourceOverride)
- {
if (!this._saveLocalResourceOverridesDebouncer) {
this._pendingLocalResourceOverrideSaves = new Set;
this._saveLocalResourceOverridesDebouncer = new Debouncer(() => {
diff --git a/Source/WebInspectorUI/UserInterface/Main.html b/Source/WebInspectorUI/UserInterface/Main.html
index 8a97915..9bb49b1 100644
--- a/Source/WebInspectorUI/UserInterface/Main.html
+++ b/Source/WebInspectorUI/UserInterface/Main.html
@@ -94,6 +94,7 @@
<link rel="stylesheet" href="Views/DefaultDashboardView.css">
<link rel="stylesheet" href="Views/DetailsSection.css">
<link rel="stylesheet" href="Views/DividerNavigationItem.css">
+ <link rel="stylesheet" href="Views/DropZoneView.css">
<link rel="stylesheet" href="Views/Editing.css">
<link rel="stylesheet" href="Views/ErrorObjectView.css">
<link rel="stylesheet" href="Views/EventBreakpointPopover.css">
@@ -290,6 +291,7 @@
<script src="Base/Object.js"></script>
<script src="Base/Throttler.js"></script>
+ <script src="Base/BlobUtilities.js"></script>
<script src="Base/DOMUtilities.js"></script>
<script src="Base/EventListener.js"></script>
<script src="Base/EventListenerSet.js"></script>
@@ -660,6 +662,7 @@
<script src="Views/DebuggerDashboardView.js"></script>
<script src="Views/DefaultDashboardView.js"></script>
<script src="Views/DividerNavigationItem.js"></script>
+ <script src="Views/DropZoneView.js"></script>
<script src="Views/EditableDataGridNode.js"></script>
<script src="Views/EditingSupport.js"></script>
<script src="Views/ErrorObjectView.js"></script>
diff --git a/Source/WebInspectorUI/UserInterface/Models/LocalResource.js b/Source/WebInspectorUI/UserInterface/Models/LocalResource.js
index 666f0db..f51adb4 100644
--- a/Source/WebInspectorUI/UserInterface/Models/LocalResource.js
+++ b/Source/WebInspectorUI/UserInterface/Models/LocalResource.js
@@ -74,10 +74,6 @@
this._responseBodyTransferSize = !isNaN(metrics.responseBodyBytesReceived) ? metrics.responseBodyBytesReceived : NaN;
this._responseBodySize = !isNaN(metrics.responseBodyDecodedSize) ? metrics.responseBodyDecodedSize : NaN;
- // Access to the content.
- this._localContent = response.content;
- this._localContentIsBase64Encoded = response.base64Encoded;
-
// LocalResource specific.
this._isLocalResourceOverride = isLocalResourceOverride || false;
@@ -87,7 +83,9 @@
this._cached = false; // FIXME: How should we denote cached? Assume from response source?
// Finalize WI.SourceCode.
- this._originalRevision = new WI.SourceCodeRevision(this, this._localContent);
+ let content = response.content;
+ let base64Encoded = response.base64Encoded;
+ this._originalRevision = new WI.SourceCodeRevision(this, content, base64Encoded, this._mimeType);
this._currentRevision = this._originalRevision;
}
@@ -223,8 +221,8 @@
mimeType: this.mimeType,
statusCode: this.statusCode,
statusText: this.statusText,
- content: this._localContent,
- base64Encoded: this._localContentIsBase64Encoded,
+ content: this.currentRevision.content,
+ base64Encoded: this.currentRevision.base64Encoded,
},
isLocalResourceOverride: this._isLocalResourceOverride,
};
@@ -232,58 +230,27 @@
// Public
- get localContent() { return this._localContent; }
- get localContentIsBase64Encoded() { return this._localContentIsBase64Encoded; }
-
get isLocalResourceOverride()
{
return this._isLocalResourceOverride;
}
- hasContent()
- {
- return !!this._localContent;
- }
-
- setContent(content, base64Encoded)
- {
- console.assert(!this._localContent);
-
- // The backend may send base64 encoded data for text resources.
- // If that is the case decode them here and treat as text.
- if (base64Encoded && WI.shouldTreatMIMETypeAsText(this._mimeType)) {
- content = atob(content);
- base64Encoded = false;
- }
-
- this._localContent = content;
- this._localContentIsBase64Encoded = base64Encoded;
- }
-
- updateOverrideContent(content, base64Encoded, mimeType, options = {})
- {
- console.assert(this._isLocalResourceOverride);
-
- if (content !== undefined && this._localContent !== content)
- this._localContent = content;
-
- if (base64Encoded !== undefined && this._localContentIsBase64Encoded !== base64Encoded)
- this._localContentIsBase64Encoded = base64Encoded;
-
- if (mimeType !== undefined && mimeType !== this._mimeType) {
- let oldMIMEType = this._mimeType;
- this._mimeType = mimeType;
- this.dispatchEventToListeners(WI.Resource.Event.MIMETypeDidChange, {oldMIMEType});
- }
- }
-
// Protected
requestContentFromBackend()
{
return Promise.resolve({
- content: this._localContent,
- base64Encoded: this._localContentIsBase64Encoded,
+ content: this._originalRevision.content,
+ base64Encoded: this._originalRevision.base64Encoded,
});
}
+
+ handleCurrentRevisionContentChange()
+ {
+ if (this._mimeType !== this.currentRevision.mimeType) {
+ let oldMIMEType = this._mimeType;
+ this._mimeType = this.currentRevision.mimeType;
+ this.dispatchEventToListeners(WI.Resource.Event.MIMETypeDidChange, {oldMIMEType});
+ }
+ }
};
diff --git a/Source/WebInspectorUI/UserInterface/Models/Resource.js b/Source/WebInspectorUI/UserInterface/Models/Resource.js
index 4feddc6..52aa0b8 100644
--- a/Source/WebInspectorUI/UserInterface/Models/Resource.js
+++ b/Source/WebInspectorUI/UserInterface/Models/Resource.js
@@ -422,21 +422,14 @@
createObjectURL()
{
+ let revision = this.currentRevision;
+ let blobContent = revision.blobContent;
+ if (blobContent)
+ return URL.createObjectURL(blobContent)
+
// If content is not available, fallback to using original URL.
// The client may try to revoke it, but nothing will happen.
- let content = this.content;
- if (!content)
- return this._url;
-
- if (content instanceof Blob)
- return URL.createObjectURL(content);
-
- if (typeof content === "string") {
- let blob = textToBlob(content, this._mimeType);
- return URL.createObjectURL(blob);
- }
-
- return null;
+ return this._url;
}
isMainResource()
@@ -1065,7 +1058,7 @@
cookie[WI.Resource.MainResourceCookieKey] = this.isMainResource();
}
- async createLocalResourceOverride(initialContent)
+ async createLocalResourceOverride({initialContent} = {})
{
console.assert(!this.isLocalResourceOverride);
console.assert(WI.NetworkManager.supportsLocalResourceOverrides());
diff --git a/Source/WebInspectorUI/UserInterface/Models/Script.js b/Source/WebInspectorUI/UserInterface/Models/Script.js
index fa298eb..967b565 100644
--- a/Source/WebInspectorUI/UserInterface/Models/Script.js
+++ b/Source/WebInspectorUI/UserInterface/Models/Script.js
@@ -117,7 +117,7 @@
get mimeType()
{
- return this._resource.mimeType;
+ return this._resource ? this._resource.mimeType : "text/javascript";
}
get isScript()
diff --git a/Source/WebInspectorUI/UserInterface/Models/SourceCode.js b/Source/WebInspectorUI/UserInterface/Models/SourceCode.js
index 60278be..af78650 100644
--- a/Source/WebInspectorUI/UserInterface/Models/SourceCode.js
+++ b/Source/WebInspectorUI/UserInterface/Models/SourceCode.js
@@ -29,7 +29,7 @@
{
super();
- this._originalRevision = new WI.SourceCodeRevision(this, null, false);
+ this._originalRevision = new WI.SourceCodeRevision(this);
this._currentRevision = this._originalRevision;
this._sourceMaps = null;
@@ -231,17 +231,22 @@
{
// Different backend APIs return one of `content, `body`, `text`, or `scriptSource`.
let rawContent = parameters.content || parameters.body || parameters.text || parameters.scriptSource;
+ let rawBase64Encoded = !!parameters.base64Encoded;
let content = rawContent;
let error = parameters.error;
let message = parameters.message;
if (parameters.base64Encoded)
- content = content ? decodeBase64ToBlob(content, this.mimeType) : "";
+ content = content ? WI.BlobUtilities.decodeBase64ToBlob(content, this.mimeType) : "";
let revision = this.revisionForRequestedContent;
this._ignoreRevisionContentDidChangeEvent = true;
- revision.content = content || null;
+ revision.updateRevisionContent(rawContent, {
+ base64Encoded: rawBase64Encoded,
+ mimeType: this.mimeType,
+ blobContent: content instanceof Blob ? content : null,
+ });
this._ignoreRevisionContentDidChangeEvent = false;
this._initializeCurrentRevisionIfNeeded();
@@ -249,6 +254,8 @@
// FIXME: Returning the content in this promise is misleading. It may not be current content
// now, and it may become out-dated later on. We should drop content from this promise
// and require clients to ask for the current contents from the sourceCode in the result.
+ // That would also avoid confusion around `content` being a Blob and eliminate the work
+ // of creating the Blob if it is not used.
return Promise.resolve({
error,
@@ -256,7 +263,7 @@
sourceCode: this,
content,
rawContent,
- rawBase64Encoded: parameters.base64Encoded,
+ rawBase64Encoded,
});
}
};
diff --git a/Source/WebInspectorUI/UserInterface/Models/SourceCodeRevision.js b/Source/WebInspectorUI/UserInterface/Models/SourceCodeRevision.js
index bda3e0f..7a52cde 100644
--- a/Source/WebInspectorUI/UserInterface/Models/SourceCodeRevision.js
+++ b/Source/WebInspectorUI/UserInterface/Models/SourceCodeRevision.js
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -25,36 +25,56 @@
WI.SourceCodeRevision = class SourceCodeRevision extends WI.Revision
{
- constructor(sourceCode, content)
+ constructor(sourceCode, content, base64Encoded, mimeType)
{
super();
console.assert(sourceCode instanceof WI.SourceCode);
+ console.assert(content === undefined || typeof content === "string");
+ console.assert(base64Encoded === undefined || typeof base64Encoded === "boolean");
+ console.assert(mimeType === undefined || typeof mimeType === "string");
this._sourceCode = sourceCode;
+
this._content = content || "";
+ this._base64Encoded = !!base64Encoded;
+ this._mimeType = mimeType;
+ this._blobContent = null;
}
// Public
- get sourceCode()
+ get sourceCode() { return this._sourceCode; }
+ get content() { return this._content; }
+ get base64Encoded() { return this._base64Encoded; }
+ get mimeType() { return this._mimeType; }
+
+ get blobContent()
{
- return this._sourceCode;
+ if (!this._blobContent && this._content)
+ this._blobContent = WI.BlobUtilities.blobForContent(this._content, this._base64Encoded, this._mimeType);
+
+ console.assert(!this._blobContent || this._blobContent instanceof Blob);
+ return this._blobContent;
}
- get content()
+ updateRevisionContent(content, {base64Encoded, mimeType, blobContent} = {})
{
- return this._content;
- }
+ console.assert(content === undefined || typeof content === "string");
+ this._content = content || "";
- set content(content)
- {
- content = content || "";
+ if (base64Encoded !== undefined) {
+ console.assert(typeof base64Encoded === "boolean");
+ this._base64Encoded = !!base64Encoded;
+ }
- if (this._content === content)
- return;
+ if (mimeType !== undefined) {
+ console.assert(typeof mimeType === "string");
+ this._mimeType = mimeType;
+ }
- this._content = content;
+ console.assert(!blobContent || blobContent instanceof Blob);
+ this._blobContent = blobContent !== undefined ? blobContent : null;
this._sourceCode.revisionContentDidChange(this);
}
@@ -71,6 +91,6 @@
copy()
{
- return new WI.SourceCodeRevision(this._sourceCode, this._content);
+ return new WI.SourceCodeRevision(this._sourceCode, this._content, this._base64Encoded, this._mimeType);
}
};
diff --git a/Source/WebInspectorUI/UserInterface/Test.html b/Source/WebInspectorUI/UserInterface/Test.html
index ad4765c..660ece2 100644
--- a/Source/WebInspectorUI/UserInterface/Test.html
+++ b/Source/WebInspectorUI/UserInterface/Test.html
@@ -54,6 +54,7 @@
<script src="Test/TestAppController.js"></script>
<script src="Test/TestUtilities.js"></script>
+ <script src="Base/BlobUtilities.js"></script>
<script src="Base/DOMUtilities.js"></script>
<script src="Base/EventListener.js"></script>
<script src="Base/EventListenerSet.js"></script>
diff --git a/Source/WebInspectorUI/UserInterface/Views/DropZoneView.css b/Source/WebInspectorUI/UserInterface/Views/DropZoneView.css
new file mode 100644
index 0000000..ec7e1cf
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Views/DropZoneView.css
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.drop-zone {
+ display: none;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: var(--z-index-glass-pane-for-drag);
+}
+
+.drop-zone.visible {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-size: 60px;
+ text-align: center;
+ color: white;
+ text-shadow: 0 1px black;
+ background-color: hsla(0, 0%, 50%, 0.50);
+ border: 3px dashed hsla(0, 0%, 100%, 0.9);
+ -webkit-backdrop-filter: blur(10px);
+}
+
+@media (prefers-color-scheme: dark) {
+ .drop-zone.visible {
+ color: hsl(0, 0%, 80%);
+ text-shadow: 0 1px hsl(0, 0%, 20%);
+ background-color: hsla(0, 0%, 30%, 0.5);
+ border: 3px dashed hsla(0, 0%, 65%, 0.9);
+ }
+}
diff --git a/Source/WebInspectorUI/UserInterface/Views/DropZoneView.js b/Source/WebInspectorUI/UserInterface/Views/DropZoneView.js
new file mode 100644
index 0000000..ea35d7d
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Views/DropZoneView.js
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// DropZoneView creates an invisible drop zone when a drag enters a target element.
+// There are delegate methods for deciding if the drop zone should appear, drag
+// progress such as entering and leaving, and the drop itself. Clients should
+// always initialize with a delegate and set a target element before showing.
+
+WI.DropZoneView = class DropZoneView extends WI.View
+{
+ constructor(delegate, {text} = {})
+ {
+ console.assert(delegate);
+ console.assert(typeof delegate.dropZoneShouldAppearForDragEvent === "function");
+
+ super();
+
+ this._delegate = delegate;
+ this._targetElement = null;
+ this._activelyHandlingDrag = false;
+
+ if (text)
+ this.element.textContent = text;
+
+ this.element.classList.add("drop-zone");
+ }
+
+ // Public
+
+ get delegate() { return this._delegate; }
+
+ get targetElement()
+ {
+ return this._targetElement;
+ }
+
+ set targetElement(element)
+ {
+ console.assert(!this._activelyHandlingDrag);
+
+ if (this._targetElement === element)
+ return;
+
+ if (!this._boundHandleDragEnter)
+ this._boundHandleDragEnter = this._handleDragEnter.bind(this);
+
+ if (this._targetElement)
+ this._targetElement.removeEventListener("dragenter", this._boundHandleDragEnter);
+
+ this._targetElement = element;
+
+ if (this._targetElement)
+ this._targetElement.addEventListener("dragenter", this._boundHandleDragEnter);
+ }
+
+ // Protected
+
+ initialLayout()
+ {
+ super.initialLayout();
+
+ console.assert(this._targetElement);
+
+ this.element.addEventListener("dragover", this._handleDragOver.bind(this));
+ this.element.addEventListener("dragleave", this._handleDragLeave.bind(this));
+ this.element.addEventListener("drop", this._handleDrop.bind(this));
+ }
+
+ // Private
+
+ _startActiveDrag()
+ {
+ console.assert(!this._activelyHandlingDrag);
+ this._activelyHandlingDrag = true;
+ this.element.classList.add("visible");
+ }
+
+ _stopActiveDrag()
+ {
+ console.assert(this._activelyHandlingDrag);
+ this._activelyHandlingDrag = false;
+ this.element.classList.remove("visible");
+ }
+
+ _handleDragEnter(event)
+ {
+ console.assert(this.isAttached);
+ if (this._activelyHandlingDrag)
+ return;
+
+ if (!this._delegate.dropZoneShouldAppearForDragEvent(this, event))
+ return;
+
+ this._startActiveDrag();
+
+ if (this._delegate.dropZoneHandleDragEnter)
+ this._delegate.dropZoneHandleDragEnter(this, event);
+ }
+
+ _handleDragLeave(event)
+ {
+ if (!this._activelyHandlingDrag)
+ return;
+
+ this._stopActiveDrag();
+
+ if (this._delegate.dropZoneHandleDragLeave)
+ this._delegate.dropZoneHandleDragLeave(this, event);
+ }
+
+ _handleDragOver(event)
+ {
+ if (!this._activelyHandlingDrag)
+ return;
+
+ event.preventDefault();
+
+ event.dataTransfer.dropEffect = "copy";
+ }
+
+ _handleDrop(event)
+ {
+ if (!this._activelyHandlingDrag)
+ return;
+
+ event.preventDefault();
+
+ this._stopActiveDrag();
+
+ if (this._delegate.dropZoneHandleDrop)
+ this._delegate.dropZoneHandleDrop(this, event);
+ }
+};
diff --git a/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.css b/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.css
index 29661a3..18d49f2 100644
--- a/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.css
+++ b/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.css
@@ -25,13 +25,25 @@
.content-view.resource.font {
display: flex;
-
+ flex-direction: column;
justify-content: center;
-
+ align-items: center;
overflow-x: hidden;
overflow-y: auto;
}
+.content-view.resource.font > .drop-zone {
+ top: calc(var(--navigation-bar-height) - 2px); /* borders */
+}
+
+.content-view.resource.font > .preview-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+}
+
.content-view.resource.font .preview {
font-size: 72px;
font-family: serif;
@@ -42,8 +54,6 @@
overflow there will be space on all sides. It also avoids having to account
for the padding/margin in FontResourceContentView.siteToFit. */
border: 15px solid transparent;
-
- margin: auto 0;
}
.content-view.resource.font .preview > .line {
diff --git a/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.js b/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.js
index 9315a5c..112d98f 100644
--- a/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.js
@@ -31,15 +31,14 @@
this._styleElement = null;
this._previewElement = null;
+ this._previewContainer = null;
+
+ if (this.showingLocalResourceOverride)
+ this._dropZoneView = new WI.DropZoneView(this, {text: WI.UIString("Drop Font")});
}
// Public
- get previewElement()
- {
- return this._previewElement;
- }
-
sizeToFit()
{
if (!this._previewElement)
@@ -56,68 +55,29 @@
contentAvailable(content, base64Encoded)
{
this._fontObjectURL = this.resource.createObjectURL();
+
if (!this._fontObjectURL) {
this.showGenericErrorMessage();
return;
}
- const uniqueFontName = "WebInspectorFontPreview" + (++WI.FontResourceContentView._uniqueFontIdentifier);
-
- var format = "";
-
- // We need to specify a format when loading SVG fonts to make them work.
- if (this.resource.mimeTypeComponents.type === "image/svg+xml")
- format = " format(\"svg\")";
-
- if (this._styleElement && this._styleElement.parentNode)
- this._styleElement.parentNode.removeChild(this._styleElement);
-
this.removeLoadingIndicator();
- this._styleElement = document.createElement("style");
- this._styleElement.textContent = "@font-face { font-family: \"" + uniqueFontName + "\"; src: url(" + this._fontObjectURL + ")" + format + "; }";
+ this._previewContainer = this.element.appendChild(document.createElement("div"));
+ this._previewContainer.className = "preview-container";
- // The style element will be added when shown later if we are not visible now.
- if (this.visible)
- document.head.appendChild(this._styleElement);
+ this._updatePreviewElement();
- this._previewElement = document.createElement("div");
- this._previewElement.className = "preview";
- this._previewElement.style.fontFamily = uniqueFontName;
-
- function createMetricElement(className)
- {
- var metricElement = document.createElement("div");
- metricElement.className = "metric " + className;
- return metricElement;
+ if (this._dropZoneView) {
+ this._dropZoneView.targetElement = this._previewContainer;
+ this.addSubview(this._dropZoneView);
}
-
- var lines = WI.FontResourceContentView.PreviewLines;
- for (var i = 0; i < lines.length; ++i) {
- var lineElement = document.createElement("div");
- lineElement.className = "line";
-
- lineElement.appendChild(createMetricElement("top"));
- lineElement.appendChild(createMetricElement("xheight"));
- lineElement.appendChild(createMetricElement("middle"));
- lineElement.appendChild(createMetricElement("baseline"));
- lineElement.appendChild(createMetricElement("bottom"));
-
- var contentElement = document.createElement("div");
- contentElement.className = "content";
- contentElement.textContent = lines[i];
- lineElement.appendChild(contentElement);
-
- this._previewElement.appendChild(lineElement);
- }
-
- this.element.appendChild(this._previewElement);
-
- this.sizeToFit();
}
shown()
{
+ super.shown();
+
// Add the style element since it is removed when hidden.
if (this._styleElement)
document.head.appendChild(this._styleElement);
@@ -126,8 +86,10 @@
hidden()
{
// Remove the style element so it will not stick around when this content view is destroyed.
- if (this._styleElement && this._styleElement.parentNode)
- this._styleElement.parentNode.removeChild(this._styleElement);
+ if (this._styleElement)
+ this._styleElement.remove();
+
+ super.hidden();
}
closed()
@@ -138,6 +100,8 @@
// the object URL to be resolved again.
if (this._fontObjectURL)
URL.revokeObjectURL(this._fontObjectURL);
+
+ super.closed();
}
// Protected
@@ -146,6 +110,104 @@
{
this.sizeToFit();
}
+
+ // DropZoneView delegate
+
+ dropZoneShouldAppearForDragEvent(dropZone, event)
+ {
+ return event.dataTransfer.types.includes("Files");
+ }
+
+ dropZoneHandleDrop(dropZone, event)
+ {
+ let files = event.dataTransfer.files;
+ let file = files.length === 1 ? files[0] : null;
+ if (!file) {
+ InspectorFrontendHost.beep();
+ return;
+ }
+
+ let fileReader = new FileReader;
+ fileReader.addEventListener("loadend", (event) => {
+ 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);
+ }
+
+ let revision = localResourceOverride.localResource.currentRevision;
+ revision.updateRevisionContent(data, {base64Encoded: base64, mimeType});
+
+ this._fontObjectURL = this.resource.createObjectURL();
+ this._updatePreviewElement();
+ });
+ fileReader.readAsDataURL(file);
+ }
+
+ // Private
+
+ _updatePreviewElement()
+ {
+ if (this._styleElement)
+ this._styleElement.remove();
+ if (this._previewElement)
+ this._previewElement.remove();
+
+ const uniqueFontName = "WebInspectorFontPreview" + (++WI.FontResourceContentView._uniqueFontIdentifier);
+
+ let format = "";
+
+ // We need to specify a format when loading SVG fonts to make them work.
+ if (this.resource.mimeTypeComponents.type === "image/svg+xml")
+ format = " format(\"svg\")";
+
+ this._styleElement = document.createElement("style");
+ this._styleElement.textContent = `@font-face { font-family: "${uniqueFontName}"; src: url(${this._fontObjectURL}) ${format}; }`;
+
+ // The style element will be added when shown later if we are not visible now.
+ if (this.visible)
+ document.head.appendChild(this._styleElement);
+
+ this._previewElement = document.createElement("div");
+ this._previewElement.className = "preview";
+ this._previewElement.style.fontFamily = uniqueFontName;
+
+ function createMetricElement(className) {
+ let metricElement = document.createElement("div");
+ metricElement.className = "metric " + className;
+ return metricElement;
+ }
+
+ for (let line of WI.FontResourceContentView.PreviewLines) {
+ let lineElement = document.createElement("div");
+ lineElement.className = "line";
+
+ lineElement.appendChild(createMetricElement("top"));
+ lineElement.appendChild(createMetricElement("xheight"));
+ lineElement.appendChild(createMetricElement("middle"));
+ lineElement.appendChild(createMetricElement("baseline"));
+ lineElement.appendChild(createMetricElement("bottom"));
+
+ let contentElement = document.createElement("div");
+ contentElement.className = "content";
+ contentElement.textContent = line;
+ lineElement.appendChild(contentElement);
+
+ this._previewElement.appendChild(lineElement);
+ }
+
+ this._previewContainer.appendChild(this._previewElement);
+
+ this.sizeToFit();
+ }
};
WI.FontResourceContentView._uniqueFontIdentifier = 0;
diff --git a/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.css b/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.css
index 9e6bc04..5c401a18 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.css
+++ b/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.css
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -24,26 +24,33 @@
*/
.content-view.resource.image {
- background-color: hsl(0, 0%, 90%);
-
- overflow-x: hidden;
- overflow-y: auto;
-
display: flex;
-
+ flex-direction: column;
justify-content: center;
align-items: center;
+ background-color: hsl(0, 0%, 90%);
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+.content-view.resource.image > .drop-zone {
+ top: calc(var(--navigation-bar-height) - 2px); /* borders */
+}
+
+.content-view.resource.image > .img-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
padding: 15px;
}
.content-view.resource.image img {
max-width: 100%;
-
+ max-height: 100%;
-webkit-user-select: text;
-webkit-user-drag: auto;
-
- margin: auto 0;
}
@media (prefers-color-scheme: dark) {
diff --git a/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.js b/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.js
index ba94214..58f499b 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.js
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,9 +27,12 @@
{
constructor(resource)
{
+ console.assert(resource instanceof WI.Resource);
+
super(resource, "image");
this._imageElement = null;
+ this._draggingInternalImageElement = false;
const toolTip = WI.UIString("Show transparency grid");
const activatedToolTip = WI.UIString("Hide transparency grid");
@@ -37,13 +40,20 @@
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
get navigationItems()
{
- return [this._showGridButtonNavigationItem];
+ let items = super.navigationItems;
+
+ items.push(this._showGridButtonNavigationItem);
+
+ return items;
}
contentAvailable(content, base64Encoded)
@@ -61,13 +71,28 @@
return;
}
- this._imageElement = document.createElement("img");
+ let imageContainer = this.element.appendChild(document.createElement("div"));
+ imageContainer.className = "img-container";
+
+ this._imageElement = imageContainer.appendChild(document.createElement("img"));
this._imageElement.addEventListener("load", function() { URL.revokeObjectURL(objectURL); });
this._imageElement.src = objectURL;
this._imageElement.setAttribute("filename", this.resource.urlComponents.lastPathComponent || "");
this._updateImageGrid();
- this.element.appendChild(this._imageElement);
+ this._imageElement.addEventListener("dragstart", (event) => {
+ console.assert(!this._draggingInternalImageElement);
+ this._draggingInternalImageElement = true;
+ });
+ this._imageElement.addEventListener("dragend", (event) => {
+ console.assert(this._draggingInternalImageElement);
+ this._draggingInternalImageElement = false;
+ });
+
+ if (this._dropZoneView) {
+ this._dropZoneView.targetElement = imageContainer;
+ this.addSubview(this._dropZoneView);
+ }
}
// Protected
@@ -88,6 +113,58 @@
super.hidden();
}
+ closed()
+ {
+ WI.networkManager.removeEventListener(null, null, this);
+
+ super.closed();
+ }
+
+ // DropZoneView delegate
+
+ dropZoneShouldAppearForDragEvent(dropZone, event)
+ {
+ // Do not appear if the drag is the current image inside this view.
+ if (this._draggingInternalImageElement)
+ return false;
+
+ // Appear if the drop contains a file.
+ return event.dataTransfer.types.includes("Files");
+ }
+
+ dropZoneHandleDrop(dropZone, event)
+ {
+ let files = event.dataTransfer.files;
+ let file = files.length === 1 ? files[0] : null;
+ if (!file) {
+ InspectorFrontendHost.beep();
+ return;
+ }
+
+ let fileReader = new FileReader;
+ fileReader.addEventListener("loadend", (event) => {
+ 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);
+ }
+
+ let revision = localResourceOverride.localResource.currentRevision;
+ revision.updateRevisionContent(data, {base64Encoded: base64, mimeType});
+ });
+ fileReader.readAsDataURL(file);
+ }
+
// Private
_updateImageGrid()
diff --git a/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverridePopover.js b/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverridePopover.js
index ec0374e..c6101e4 100644
--- a/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverridePopover.js
+++ b/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverridePopover.js
@@ -118,7 +118,7 @@
if (!statusCode || statusCode === "NaN")
statusCode = "200";
if (!statusText)
- statusText = WI.HTTPUtilities.statusTextForStatusCode(statusCode);
+ statusText = WI.HTTPUtilities.statusTextForStatusCode(parseInt(statusCode));
let popoverContentElement = document.createElement("div");
popoverContentElement.className = "local-resource-override-popover-content";
diff --git a/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverrideTreeElement.js b/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverrideTreeElement.js
index 046c8e8..2f9dbc7 100644
--- a/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverrideTreeElement.js
+++ b/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverrideTreeElement.js
@@ -125,14 +125,15 @@
let wasSelected = this.selected;
+ let revision = this._localResourceOverride.localResource.currentRevision;
let newLocalResourceOverride = WI.LocalResourceOverride.create({
url,
mimeType,
statusCode,
statusText,
headers,
- content: this._localResourceOverride.localResource.localContent,
- base64Encoded: this._localResourceOverride.localResource.localContentIsBase64Encoded,
+ content: revision.content,
+ base64Encoded: revision.base64Encoded,
});
WI.networkManager.removeLocalResourceOverride(this._localResourceOverride);
diff --git a/Source/WebInspectorUI/UserInterface/Views/ResourceContentView.js b/Source/WebInspectorUI/UserInterface/Views/ResourceContentView.js
index 9bb31b3..82b361b 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ResourceContentView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ResourceContentView.js
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -57,13 +57,48 @@
for (var i = 0; i < issues.length; ++i)
this.addIssue(issues[i]);
}
+
+ this._showingLocalResourceOverride = false;
+
+ if (WI.NetworkManager.supportsLocalResourceOverrides()) {
+ if (resource.isLocalResourceOverride) {
+ this._showingLocalResourceOverride = true;
+
+ this._localResourceOverrideBannerView = new WI.LocalResourceOverrideLabelView(resource);
+
+ 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);
+
+ this._createLocalResourceOverrideButtonNavigationItem = new WI.ButtonNavigationItem("create-local-resource-override", WI.UIString("Create Local Override"), "Images/NavigationItemNetworkOverride.svg", 13, 14);
+ this._createLocalResourceOverrideButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleCreateLocalResourceOverride, this);
+ this._createLocalResourceOverrideButtonNavigationItem.enabled = false; // Enabled when the content is available.
+ this._createLocalResourceOverrideButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
+ }
+
+ WI.networkManager.addEventListener(WI.NetworkManager.Event.LocalResourceOverrideAdded, this._handleLocalResourceOverrideChanged, this);
+ WI.networkManager.addEventListener(WI.NetworkManager.Event.LocalResourceOverrideRemoved, this._handleLocalResourceOverrideChanged, this);
+ }
}
// Public
- get resource()
+ get resource() { return this._resource; }
+ get showingLocalResourceOverride() { return this._showingLocalResourceOverride; }
+
+ get navigationItems()
{
- return this._resource;
+ let items = [];
+
+ if (this._removeLocalResourceOverrideButtonNavigationItem)
+ items.push(this._removeLocalResourceOverrideButtonNavigationItem);
+ if (this._createLocalResourceOverrideButtonNavigationItem)
+ items.push(this._createLocalResourceOverrideButtonNavigationItem);
+
+ return items;
}
get supportsSave()
@@ -81,6 +116,12 @@
throw WI.NotImplementedError.subclassMustOverride();
}
+ localResourceOverrideInitialContent()
+ {
+ // Implemented by subclasses if needed.
+ return {};
+ }
+
showGenericNoContentMessage()
{
this.showMessage(WI.UIString("Resource has no content"));
@@ -110,6 +151,9 @@
{
super.closed();
+ if (WI.NetworkManager.supportsLocalResourceOverrides())
+ WI.networkManager.removeEventListener(null, null, this);
+
if (!this.managesOwnIssues)
WI.consoleManager.removeEventListener(null, null, this);
}
@@ -123,7 +167,10 @@
this._spinnerTimeout = undefined;
}
- this.element.removeChildren();
+ this.removeAllSubviews();
+
+ if (this._localResourceOverrideBannerView)
+ this.addSubview(this._localResourceOverrideBannerView);
}
// Private
@@ -144,6 +191,9 @@
console.assert(!this._hasContent());
console.assert(parameters.sourceCode === this._resource);
this.contentAvailable(parameters.sourceCode.content, parameters.base64Encoded);
+
+ if (this._createLocalResourceOverrideButtonNavigationItem)
+ this._createLocalResourceOverrideButtonNavigationItem.enabled = WI.networkManager.canBeOverridden(this._resource);
}
_contentError(error)
@@ -168,15 +218,40 @@
console.assert(!this.managesOwnIssues);
var issue = event.data.issue;
- if (!WI.ConsoleManager.issueMatchSourceCode(issue, this.resource))
+ if (!WI.ConsoleManager.issueMatchSourceCode(issue, this._resource))
return;
this.addIssue(issue);
}
+ async _handleCreateLocalResourceOverride(event)
+ {
+ let initialContent = this.localResourceOverrideInitialContent();
+ let localResourceOverride = await this._resource.createLocalResourceOverride(initialContent);
+ WI.networkManager.addLocalResourceOverride(localResourceOverride);
+ WI.showLocalResourceOverride(localResourceOverride);
+ }
+
+ _handleRemoveLocalResourceOverride(event)
+ {
+ console.assert(this._showingLocalResourceOverride);
+
+ let localResourceOverride = WI.networkManager.localResourceOverrideForURL(this._resource.url);
+ WI.networkManager.removeLocalResourceOverride(localResourceOverride);
+ }
+
+ _handleLocalResourceOverrideChanged(event)
+ {
+ if (this._resource.url !== event.data.localResourceOverride.url)
+ return;
+
+ if (this._createLocalResourceOverrideButtonNavigationItem)
+ this._createLocalResourceOverrideButtonNavigationItem.enabled = WI.networkManager.canBeOverridden(this._resource);
+ }
+
_mouseWasClicked(event)
{
- WI.handlePossibleLinkClick(event, this.resource.parentFrame);
+ WI.handlePossibleLinkClick(event, this._resource.parentFrame);
}
};
diff --git a/Source/WebInspectorUI/UserInterface/Views/TextResourceContentView.js b/Source/WebInspectorUI/UserInterface/Views/TextResourceContentView.js
index 77073db..dec2408 100644
--- a/Source/WebInspectorUI/UserInterface/Views/TextResourceContentView.js
+++ b/Source/WebInspectorUI/UserInterface/Views/TextResourceContentView.js
@@ -56,27 +56,6 @@
this._codeCoverageButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
WI.settings.enableControlFlowProfiler.addEventListener(WI.Setting.Event.Changed, this._enableControlFlowProfilerSettingChanged, this);
- this._showingLocalResourceOverride = false;
-
- if (WI.NetworkManager.supportsLocalResourceOverrides()) {
- if (resource instanceof WI.Resource && resource.isLocalResourceOverride) {
- this._showingLocalResourceOverride = true;
- this._localResourceOverrideBannerView = new WI.LocalResourceOverrideLabelView(resource);
-
- 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);
-
- this._createLocalResourceOverrideButtonNavigationItem = new WI.ButtonNavigationItem("create-local-resource-override", WI.UIString("Create Local Override"), "Images/NavigationItemNetworkOverride.svg", 13, 14);
- this._createLocalResourceOverrideButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleCreateLocalResourceOverride, this);
- this._createLocalResourceOverrideButtonNavigationItem.enabled = false; // Enabled when the text editor is populated with content.
- this._createLocalResourceOverrideButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
- }
- }
-
this._textEditor = new WI.SourceCodeTextEditor(resource);
this._textEditor.addEventListener(WI.TextEditor.Event.ExecutionLineNumberDidChange, this._executionLineNumberDidChange, this);
this._textEditor.addEventListener(WI.TextEditor.Event.NumberOfSearchResultsDidChange, this._numberOfSearchResultsDidChange, this);
@@ -89,27 +68,17 @@
WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ProbeSetAdded, this._probeSetsChanged, this);
WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ProbeSetRemoved, this._probeSetsChanged, this);
-
- if (WI.NetworkManager.supportsLocalResourceOverrides()) {
- WI.networkManager.addEventListener(WI.NetworkManager.Event.LocalResourceOverrideAdded, this._handleLocalResourceOverrideChanged, this);
- WI.networkManager.addEventListener(WI.NetworkManager.Event.LocalResourceOverrideRemoved, this._handleLocalResourceOverrideChanged, this);
- }
}
// Public
get navigationItems()
{
- let items = [];
-
- if (this._removeLocalResourceOverrideButtonNavigationItem)
- items.push(this._removeLocalResourceOverrideButtonNavigationItem);
- if (this._createLocalResourceOverrideButtonNavigationItem)
- items.push(this._createLocalResourceOverrideButtonNavigationItem);
+ let items = super.navigationItems;
items.push(this._prettyPrintButtonNavigationItem);
- if (!this._showingLocalResourceOverride)
+ if (!this.showingLocalResourceOverride)
items.push(this._showTypesButtonNavigationItem, this._codeCoverageButtonNavigationItem);
return items;
@@ -175,6 +144,11 @@
// Do nothing.
}
+ localResourceOverrideInitialContent()
+ {
+ return {initialContent: this._textEditor.string};
+ }
+
get supportsSave()
{
return super.supportsSave || this.resource instanceof WI.CSSStyleSheet;
@@ -243,17 +217,11 @@
this.removeLoadingIndicator();
- if (this._localResourceOverrideBannerView)
- this.addSubview(this._localResourceOverrideBannerView);
-
this.addSubview(this._textEditor);
}
_contentDidPopulate(event)
{
- if (this._createLocalResourceOverrideButtonNavigationItem)
- this._createLocalResourceOverrideButtonNavigationItem.enabled = WI.networkManager.canBeOverridden(this.resource);
-
this._prettyPrintButtonNavigationItem.enabled = this._textEditor.canBeFormatted();
this._showTypesButtonNavigationItem.enabled = this._textEditor.canShowTypeAnnotations();
@@ -263,21 +231,6 @@
this._codeCoverageButtonNavigationItem.activated = WI.settings.enableControlFlowProfiler.value;
}
- async _handleCreateLocalResourceOverride(event)
- {
- let localResourceOverride = await this.resource.createLocalResourceOverride(this._textEditor.string);
- WI.networkManager.addLocalResourceOverride(localResourceOverride);
- WI.showLocalResourceOverride(localResourceOverride);
- }
-
- _handleRemoveLocalResourceOverride(event)
- {
- console.assert(this._showingLocalResourceOverride);
-
- let localResourceOverride = WI.networkManager.localResourceOverrideForURL(this.resource.url);
- WI.networkManager.removeLocalResourceOverride(localResourceOverride);
- }
-
_togglePrettyPrint(event)
{
var activated = !this._prettyPrintButtonNavigationItem.activated;
@@ -320,15 +273,6 @@
this._prettyPrintButtonNavigationItem.enabled = this._textEditor.canBeFormatted();
}
- _handleLocalResourceOverrideChanged(event)
- {
- if (this.resource.url !== event.data.localResourceOverride.url)
- return;
-
- if (this._createLocalResourceOverrideButtonNavigationItem)
- this._createLocalResourceOverrideButtonNavigationItem.enabled = WI.networkManager.canBeOverridden(this.resource);
- }
-
_sourceCodeContentDidChange(event)
{
if (this._ignoreSourceCodeContentDidChangeEvent)
@@ -340,7 +284,7 @@
_textEditorContentDidChange(event)
{
this._ignoreSourceCodeContentDidChangeEvent = true;
- this.resource.currentRevision.content = this._textEditor.string;
+ this.resource.currentRevision.updateRevisionContent(this._textEditor.string);
this._ignoreSourceCodeContentDidChangeEvent = false;
}
@@ -374,7 +318,7 @@
if (this.resource.urlComponents.scheme === "file")
return true;
- if (this._showingLocalResourceOverride)
+ if (this.showingLocalResourceOverride)
return true;
return false;