Changeset 251024 in webkit


Ignore:
Timestamp:
Oct 11, 2019, 1:44:05 PM (6 years ago)
Author:
Joseph Pecoraro
Message:

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.

Location:
trunk
Files:
2 added
27 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r251023 r251024  
     12019-10-10  Joseph Pecoraro  <pecoraro@apple.com>
     2
     3        Web Inspector: Local Resource Overrides: UI for overriding image and font resource content
     4        https://bugs.webkit.org/show_bug.cgi?id=202016
     5        <rdar://problem/55541475>
     6
     7        Reviewed by Devin Rousso.
     8
     9        * inspector/unit-tests/mimetype-utilities-expected.txt:
     10        * inspector/unit-tests/mimetype-utilities.html:
     11        Test new utilities.
     12
     13        * http/tests/inspector/network/fetch-response-body.html:
     14        * http/tests/inspector/network/xhr-response-body.html:
     15        Renamed utilities.
     16
    1172019-10-11  Dean Jackson  <dino@apple.com>
    218
  • trunk/LayoutTests/http/tests/inspector/network/fetch-response-body.html

    r225546 r251024  
    4343                return;
    4444            }
    45             blobAsText(content, (text) => {
     45            WI.BlobUtilities.blobAsText(content, (text) => {
    4646                resolve(text);
    4747            });
  • trunk/LayoutTests/http/tests/inspector/network/xhr-response-body.html

    r234702 r251024  
    5757                return;
    5858            }
    59             blobAsText(content, (text) => {
     59            WI.BlobUtilities.blobAsText(content, (text) => {
    6060                resolve(text);
    6161            });
  • trunk/LayoutTests/inspector/unit-tests/mimetype-utilities-expected.txt

    r247533 r251024  
    11
    22== Running test suite: MIMETypeUtilities
     3-- Running test case: fileExtensionForFilename
     4PASS: File extension for null filename should be null.
     5PASS: File extension for filename without a period should be null.
     6PASS: File extension for filename ending in a period should be null.
     7PASS: File extension for "foo.xyz" should be "xyz".
     8PASS: File extension for "image.png" should be "png".
     9PASS: File extension for "image.png" should be "gif".
     10PASS: File extension for "script.js" should be "js".
     11PASS: File extension for "script.min.js" should be "js".
     12
    313-- Running test case: fileExtensionForURL
    414PASS: File extension for null URL should be null.
  • trunk/LayoutTests/inspector/unit-tests/mimetype-utilities.html

    r247533 r251024  
    99
    1010    suite.addTestCase({
     11        name: "fileExtensionForFilename",
     12        test() {
     13            InspectorTest.expectNull(WI.fileExtensionForFilename(null), `File extension for null filename should be null.`);
     14            InspectorTest.expectEqual(WI.fileExtensionForURL("test"), null, `File extension for filename without a period should be null.`);
     15            InspectorTest.expectEqual(WI.fileExtensionForURL("test."), null, `File extension for filename ending in a period should be null.`);
     16
     17            InspectorTest.expectEqual(WI.fileExtensionForFilename("foo.xyz"), "xyz", `File extension for "foo.xyz" should be "xyz".`);
     18            InspectorTest.expectEqual(WI.fileExtensionForFilename("image.png"), "png", `File extension for "image.png" should be "png".`);
     19            InspectorTest.expectEqual(WI.fileExtensionForFilename("image.gif"), "gif", `File extension for "image.png" should be "gif".`);
     20            InspectorTest.expectEqual(WI.fileExtensionForFilename("script.js"), "js", `File extension for "script.js" should be "js".`);
     21            InspectorTest.expectEqual(WI.fileExtensionForFilename("script.min.js"), "js", `File extension for "script.min.js" should be "js".`);
     22
     23            return true;
     24        }
     25    });
     26
     27    suite.addTestCase({
    1128        name: "fileExtensionForURL",
    1229        test() {
    13             InspectorTest.expectEqual(WI.fileExtensionForURL(null), null, `File extension for null URL should be null.`);
     30            InspectorTest.expectNull(WI.fileExtensionForURL(null), `File extension for null URL should be null.`);
    1431            InspectorTest.expectEqual(WI.fileExtensionForURL("invalid-url"), null, `File extension for invalid URL should be null.`);
    1532            InspectorTest.expectEqual(WI.fileExtensionForURL("https://example.com"), null, `File extension for URL without last path component should be null.`);
  • trunk/Source/WebInspectorUI/.eslintrc

    r248537 r251024  
    112112        "appendWebInspectorConsoleEvaluationSourceURL": true,
    113113        "appendWebInspectorSourceURL": true,
    114         "blobAsText": true,
    115114        "clamp": true,
    116115        "doubleQuotedString": true,
     
    133132        "resolveDotsInPath": true,
    134133        "simpleGlobStringToRegExp": true,
    135         "textToBlob": true,
    136134        "timestamp": true,
    137135        "zeroWidthSpace": true,
     
    142140        // URL Utilities
    143141        "absoluteURL": true,
    144         "decodeBase64ToBlob": true,
    145142        "parseDataURL": true,
    146143        "parseLocationQueryParameters": true,
  • trunk/Source/WebInspectorUI/ChangeLog

    r250991 r251024  
     12019-10-10  Joseph Pecoraro  <pecoraro@apple.com>
     2
     3        Web Inspector: Local Resource Overrides: UI for overriding image and font resource content
     4        https://bugs.webkit.org/show_bug.cgi?id=202016
     5        <rdar://problem/55541475>
     6
     7        Reviewed by Devin Rousso.
     8
     9        Extend SourceCodeRevision to be a (content, base64Encoded, mimeType) tuple and
     10        make clients update the revision content more explicitly (`updateRevisionContent`).
     11        This also includes `blobContent` as a more explicit way to get the content as
     12        a Blob, which may not always be desired.
     13
     14        Switch LocalResource use the originalRevision / currentRevision instead of
     15        keeping its own localContent / localContentIsBase64Encoded properties.
     16
     17        Introduce a `DropZoneView` to simplify handling of presenting a drop zone
     18        over a specific element. And use it for the ImageResourceContentView for local
     19        resource overrides to accept new content.
     20
     21        * Localizations/en.lproj/localizedStrings.js:
     22        * UserInterface/Main.html:
     23        New strings and resources.
     24
     25        * .eslintrc:
     26        * UserInterface/Base/BlobUtilities.js: Added.
     27        (WI.BlobUtilities.blobForContent):
     28        (WI.BlobUtilities.decodeBase64ToBlob):
     29        (WI.BlobUtilities.textToBlob):
     30        (WI.BlobUtilities.blobAsText):
     31        (WI.BlobUtilities):
     32        * UserInterface/Base/FileUtilities.js:
     33        (WI.FileUtilities.async.readDataURL):
     34        (WI.FileUtilities):
     35        * UserInterface/Base/MIMETypeUtilities.js:
     36        (WI.fileExtensionForFilename):
     37        (WI.fileExtensionForURL):
     38        * UserInterface/Base/Utilities.js:
     39        Move around or introduce some minor utilities.
     40
     41        * UserInterface/Models/SourceCodeRevision.js:
     42        (WI.SourceCodeRevision):
     43        (WI.SourceCodeRevision.prototype.get sourceCode):
     44        (WI.SourceCodeRevision.prototype.get content):
     45        (WI.SourceCodeRevision.prototype.get base64Encoded):
     46        (WI.SourceCodeRevision.prototype.get mimeType):
     47        (WI.SourceCodeRevision.prototype.get blobContent):
     48        (WI.SourceCodeRevision.prototype.updateRevisionContent):
     49        (WI.SourceCodeRevision.prototype.copy):
     50        (WI.SourceCodeRevision.prototype.set content): Deleted.
     51        Data is now a (content, base64Encoded, mimeType) tuple.
     52
     53        * UserInterface/Controllers/NetworkManager.js:
     54        (WI.NetworkManager.prototype.responseIntercepted):
     55        (WI.NetworkManager.prototype._handleResourceContentDidChange):
     56        (WI.NetworkManager.prototype._persistLocalResourceOverrideSoonAfterContentChange): Deleted.
     57        This is now a unified resource content change path without anything special for
     58        local resource overrides.
     59
     60        * UserInterface/Models/LocalResource.js:
     61        (WI.LocalResource.prototype.toJSON):
     62        (WI.LocalResource.prototype.requestContentFromBackend):
     63        (WI.LocalResource.prototype.handleCurrentRevisionContentChange):
     64        (WI.LocalResource):
     65        (WI.LocalResource.prototype.get localContent): Deleted.
     66        (WI.LocalResource.prototype.get localContentIsBase64Encoded): Deleted.
     67        (WI.LocalResource.prototype.hasContent): Deleted.
     68        (WI.LocalResource.prototype.setContent): Deleted.
     69        (WI.LocalResource.prototype.updateOverrideContent): Deleted.
     70        Use originalRevision / currentRevision as appropriate.
     71
     72        * UserInterface/Views/DropZoneView.css: Added.
     73        (.drop-zone):
     74        (.drop-zone.visible):
     75        (@media (prefers-color-scheme: dark)):
     76        * UserInterface/Views/DropZoneView.js: Added.
     77        (WI.DropZoneView):
     78        (WI.DropZoneView.prototype.get delegate):
     79        (WI.DropZoneView.prototype.get targetElement):
     80        (WI.DropZoneView.prototype.set targetElement):
     81        (WI.DropZoneView.prototype.initialLayout):
     82        (WI.DropZoneView.prototype._startActiveDrag):
     83        (WI.DropZoneView.prototype._stopActiveDrag):
     84        (WI.DropZoneView.prototype._handleDragEnter):
     85        (WI.DropZoneView.prototype._handleDragLeave):
     86        (WI.DropZoneView.prototype._handleDragOver):
     87        (WI.DropZoneView.prototype._handleDrop):
     88        Simplified handling of a drop zone.
     89
     90        * UserInterface/Views/ResourceContentView.js:
     91        (WI.ResourceContentView.prototype.removeLoadingIndicator):
     92        More safely remove children and subviews.
     93
     94        (WI.ResourceContentView):
     95        (WI.ResourceContentView.prototype.get resource):
     96        (WI.ResourceContentView.prototype.get navigationItems):
     97        (WI.ResourceContentView.prototype.localResourceOverrideInitialContent):
     98        (WI.ResourceContentView.prototype.closed):
     99        (WI.ResourceContentView.prototype.removeLoadingIndicator):
     100        (WI.ResourceContentView.prototype._contentAvailable):
     101        (WI.ResourceContentView.prototype._issueWasAdded):
     102        (WI.ResourceContentView.prototype.async._handleCreateLocalResourceOverride):
     103        (WI.ResourceContentView.prototype._handleRemoveLocalResourceOverride):
     104        (WI.ResourceContentView.prototype._handleLocalResourceOverrideChanged):
     105        (WI.ResourceContentView.prototype._mouseWasClicked):
     106        * UserInterface/Views/TextResourceContentView.js:
     107        (WI.TextResourceContentView):
     108        (WI.TextResourceContentView.prototype.get navigationItems):
     109        (WI.TextResourceContentView.prototype.localResourceOverrideInitialContent):
     110        (WI.TextResourceContentView.prototype._contentWillPopulate):
     111        (WI.TextResourceContentView.prototype._contentDidPopulate):
     112        (WI.TextResourceContentView.prototype._textEditorContentDidChange):
     113        (WI.TextResourceContentView.prototype._shouldBeEditable):
     114        (WI.TextResourceContentView.prototype.async._handleCreateLocalResourceOverride): Deleted.
     115        (WI.TextResourceContentView.prototype._handleRemoveLocalResourceOverride): Deleted.
     116        (WI.TextResourceContentView.prototype._handleLocalResourceOverrideChanged): Deleted.
     117        Extract generalized local resource override properties into the ResourceContentView base class.
     118
     119        * UserInterface/Views/FontResourceContentView.css:
     120        (.content-view.resource.font):
     121        (.content-view.resource.font > .drop-zone):
     122        (.content-view.resource.font > .preview-container):
     123        (.content-view.resource.font .preview):
     124        * UserInterface/Views/FontResourceContentView.js:
     125        (WI.FontResourceContentView):
     126        (WI.FontResourceContentView.prototype.contentAvailable):
     127        (WI.FontResourceContentView.prototype.shown):
     128        (WI.FontResourceContentView.prototype.hidden):
     129        (WI.FontResourceContentView.prototype.closed):
     130        (WI.FontResourceContentView.prototype.layout):
     131        (WI.FontResourceContentView.prototype._updatePreviewElement.createMetricElement):
     132        (WI.FontResourceContentView.prototype._updatePreviewElement):
     133        (WI.FontResourceContentView.prototype.dropZoneShouldAppearForDragEvent):
     134        (WI.FontResourceContentView.prototype.dropZoneHandleDrop):
     135        Create a drop zone that will update the font local resource override content.
     136
     137        * UserInterface/Views/ImageResourceContentView.css:
     138        (.content-view.resource.image):
     139        (.content-view.resource.image > .drop-zone):
     140        (.content-view.resource.image > .img-container):
     141        (.content-view.resource.image img):
     142        * UserInterface/Views/ImageResourceContentView.js:
     143        (WI.ImageResourceContentView):
     144        (WI.ImageResourceContentView.prototype.get navigationItems):
     145        (WI.ImageResourceContentView.prototype.contentAvailable):
     146        (WI.ImageResourceContentView.prototype.closed):
     147        (WI.ImageResourceContentView.prototype.dropZoneShouldAppearForDragEvent):
     148        (WI.ImageResourceContentView.prototype.dropZoneHandleDrop):
     149        Create a drop zone that will update the image local resource override content.
     150
     151        * UserInterface/Models/Script.js:
     152        (WI.Script.prototype.get mimeType):
     153        Seems like this should have a default value given there may not be a resource.
     154
     155        * UserInterface/Views/LocalResourceOverridePopover.js:
     156        (WI.LocalResourceOverridePopover.prototype.show):
     157        Better handling here, since the utilities expects a number not a string.
     158
     159        * UserInterface/Models/Resource.js:
     160        (WI.Resource.prototype.createObjectURL):
     161        * UserInterface/Views/LocalResourceOverrideTreeElement.js:
     162        (WI.LocalResourceOverrideTreeElement.prototype.willDismissPopover):
     163        Use currentRevision more appropriately.
     164
     165        * UserInterface/Models/SourceCode.js:
     166        (WI.SourceCode.prototype._processContent):
     167        * UserInterface/Views/TextResourceContentView.js:
     168        (WI.TextResourceContentView.prototype._textEditorContentDidChange):
     169        * UserInterface/Controllers/CSSManager.js:
     170        (WI.CSSManager.prototype._resourceContentDidChange.applyStyleSheetChanges.styleSheetFound):
     171        (WI.CSSManager.prototype._resourceContentDidChange.applyStyleSheetChanges):
     172        (WI.CSSManager.prototype._resourceContentDidChange):
     173        (WI.CSSManager.prototype._updateResourceContent.fetchedStyleSheetContent):
     174        Update revision content more explicitly.
     175
    11762019-10-10  Devin Rousso  <drousso@apple.com>
    2177
  • trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

    r250991 r251024  
    380380localizedStrings["Download"] = "Download";
    381381localizedStrings["Download Web Archive"] = "Download Web Archive";
     382localizedStrings["Drop Font"] = "Drop Font";
     383localizedStrings["Drop Image"] = "Drop Image";
    382384localizedStrings["Dropped Element"] = "Dropped Element";
    383385localizedStrings["Dropped Node"] = "Dropped Node";
  • trunk/Source/WebInspectorUI/UserInterface/Base/MIMETypeUtilities.js

    r247533 r251024  
    2424 */
    2525
    26 WI.fileExtensionForURL = function(url)
    27 {
    28     let lastPathComponent = parseURL(url).lastPathComponent;
    29     if (!lastPathComponent)
     26WI.fileExtensionForFilename = function(filename)
     27{
     28    if (!filename)
    3029        return null;
    3130
    32     let index = lastPathComponent.lastIndexOf(".");
     31    let index = filename.lastIndexOf(".");
    3332    if (index === -1)
    3433        return null;
    3534
    36     if (index === lastPathComponent.length - 1)
     35    if (index === filename.length - 1)
    3736        return null;
    3837
    39     return lastPathComponent.substr(index + 1);
     38    return filename.substr(index + 1);
     39};
     40
     41WI.fileExtensionForURL = function(url)
     42{
     43    let lastPathComponent = parseURL(url).lastPathComponent;
     44    return WI.fileExtensionForFilename(lastPathComponent);
    4045};
    4146
  • trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js

    r249301 r251024  
    16751675    array.splice(insertionIndexForObjectInListSortedByFunction(object, array, comparator), 0, object);
    16761676}
    1677 
    1678 function decodeBase64ToBlob(base64Data, mimeType)
    1679 {
    1680     mimeType = mimeType || "";
    1681 
    1682     const sliceSize = 1024;
    1683     var byteCharacters = atob(base64Data);
    1684     var bytesLength = byteCharacters.length;
    1685     var slicesCount = Math.ceil(bytesLength / sliceSize);
    1686     var byteArrays = new Array(slicesCount);
    1687 
    1688     for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
    1689         var begin = sliceIndex * sliceSize;
    1690         var end = Math.min(begin + sliceSize, bytesLength);
    1691 
    1692         var bytes = new Array(end - begin);
    1693         for (var offset = begin, i = 0; offset < end; ++i, ++offset)
    1694             bytes[i] = byteCharacters[offset].charCodeAt(0);
    1695 
    1696         byteArrays[sliceIndex] = new Uint8Array(bytes);
    1697     }
    1698 
    1699     return new Blob(byteArrays, {type: mimeType});
    1700 }
    1701 
    1702 function textToBlob(text, mimeType)
    1703 {
    1704     return new Blob([text], {type: mimeType});
    1705 }
    1706 
    1707 function blobAsText(blob, callback)
    1708 {
    1709     console.assert(blob instanceof Blob);
    1710     let fileReader = new FileReader;
    1711     fileReader.addEventListener("loadend", () => { callback(fileReader.result); });
    1712     fileReader.readAsText(blob);
    1713 }
  • trunk/Source/WebInspectorUI/UserInterface/Controllers/CSSManager.js

    r250149 r251024  
    657657
    658658                let revision = styleSheet.currentRevision;
    659                 revision.content = resource.content;
     659                revision.updateRevisionContent(resource.content);
    660660            }
    661661
     
    702702            let revision = representedObject.currentRevision;
    703703            if (styleSheet.isInspectorStyleSheet()) {
    704                 revision.content = representedObject.content;
     704                revision.updateRevisionContent(representedObject.content);
    705705                styleSheet.dispatchEventToListeners(WI.SourceCode.Event.ContentDidChange);
    706706            } else
    707                 revision.content = parameters.content;
     707                revision.updateRevisionContent(parameters.content);
    708708
    709709            this._ignoreResourceContentDidChangeEventForResource = null;
  • trunk/Source/WebInspectorUI/UserInterface/Controllers/NetworkManager.js

    r250991 r251024  
    11/*
    2  * Copyright (C) 2013 Apple Inc. All rights reserved.
     2 * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    817817
    818818        let localResource = localResourceOverride.localResource;
    819         let content = localResource.localContent;
    820         let base64Encoded = localResource.localContentIsBase64Encoded;
    821         let {mimeType, statusCode, statusText, responseHeaders} = localResource;
     819        let revision = localResource.currentRevision;
     820
     821        let content = revision.content;
     822        let base64Encoded = revision.base64Encoded;
     823        let mimeType = revision.mimeType;
     824        let statusCode = localResource.statusCode;
     825        let statusText = localResource.statusText;
     826        let responseHeaders = localResource.responseHeaders;
     827        console.assert(revision.mimeType === localResource.mimeType);
    822828
    823829        if (isNaN(statusCode))
     
    12451251            return;
    12461252
    1247         let localResource = localResourceOverride.localResource;
    1248         let content = localResource.content;
    1249         let base64Encoded = localResource.localContentIsBase64Encoded;
    1250         let mimeType = localResource.mimeType;
    1251         localResource.updateOverrideContent(content, base64Encoded, mimeType, {suppressEvent: true});
    1252 
    1253         this._persistLocalResourceOverrideSoonAfterContentChange(localResourceOverride);
    1254     }
    1255 
    1256     _persistLocalResourceOverrideSoonAfterContentChange(localResourceOverride)
    1257     {
    12581253        if (!this._saveLocalResourceOverridesDebouncer) {
    12591254            this._pendingLocalResourceOverrideSaves = new Set;
  • trunk/Source/WebInspectorUI/UserInterface/Main.html

    r250991 r251024  
    9595    <link rel="stylesheet" href="Views/DetailsSection.css">
    9696    <link rel="stylesheet" href="Views/DividerNavigationItem.css">
     97    <link rel="stylesheet" href="Views/DropZoneView.css">
    9798    <link rel="stylesheet" href="Views/Editing.css">
    9899    <link rel="stylesheet" href="Views/ErrorObjectView.css">
     
    291292    <script src="Base/Throttler.js"></script>
    292293
     294    <script src="Base/BlobUtilities.js"></script>
    293295    <script src="Base/DOMUtilities.js"></script>
    294296    <script src="Base/EventListener.js"></script>
     
    661663    <script src="Views/DefaultDashboardView.js"></script>
    662664    <script src="Views/DividerNavigationItem.js"></script>
     665    <script src="Views/DropZoneView.js"></script>
    663666    <script src="Views/EditableDataGridNode.js"></script>
    664667    <script src="Views/EditingSupport.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Models/LocalResource.js

    r249504 r251024  
    7575        this._responseBodySize = !isNaN(metrics.responseBodyDecodedSize) ? metrics.responseBodyDecodedSize : NaN;
    7676
    77         // Access to the content.
    78         this._localContent = response.content;
    79         this._localContentIsBase64Encoded = response.base64Encoded;
    80 
    8177        // LocalResource specific.
    8278        this._isLocalResourceOverride = isLocalResourceOverride || false;
     
    8884
    8985        // Finalize WI.SourceCode.
    90         this._originalRevision = new WI.SourceCodeRevision(this, this._localContent);
     86        let content = response.content;
     87        let base64Encoded = response.base64Encoded;
     88        this._originalRevision = new WI.SourceCodeRevision(this, content, base64Encoded, this._mimeType);
    9189        this._currentRevision = this._originalRevision;
    9290    }
     
    224222                statusCode: this.statusCode,
    225223                statusText: this.statusText,
    226                 content: this._localContent,
    227                 base64Encoded: this._localContentIsBase64Encoded,
     224                content: this.currentRevision.content,
     225                base64Encoded: this.currentRevision.base64Encoded,
    228226            },
    229227            isLocalResourceOverride: this._isLocalResourceOverride,
     
    233231    // Public
    234232
    235     get localContent() { return this._localContent; }
    236     get localContentIsBase64Encoded() { return this._localContentIsBase64Encoded; }
    237 
    238233    get isLocalResourceOverride()
    239234    {
     
    241236    }
    242237
    243     hasContent()
    244     {
    245         return !!this._localContent;
    246     }
    247 
    248     setContent(content, base64Encoded)
    249     {
    250         console.assert(!this._localContent);
    251 
    252         // The backend may send base64 encoded data for text resources.
    253         // If that is the case decode them here and treat as text.
    254         if (base64Encoded && WI.shouldTreatMIMETypeAsText(this._mimeType)) {
    255             content = atob(content);
    256             base64Encoded = false;
    257         }
    258 
    259         this._localContent = content;
    260         this._localContentIsBase64Encoded = base64Encoded;
    261     }
    262 
    263     updateOverrideContent(content, base64Encoded, mimeType, options = {})
    264     {
    265         console.assert(this._isLocalResourceOverride);
    266 
    267         if (content !== undefined && this._localContent !== content)
    268             this._localContent = content;
    269 
    270         if (base64Encoded !== undefined && this._localContentIsBase64Encoded !== base64Encoded)
    271             this._localContentIsBase64Encoded = base64Encoded;
    272 
    273         if (mimeType !== undefined && mimeType !== this._mimeType) {
     238    // Protected
     239
     240    requestContentFromBackend()
     241    {
     242        return Promise.resolve({
     243            content: this._originalRevision.content,
     244            base64Encoded: this._originalRevision.base64Encoded,
     245        });
     246    }
     247
     248    handleCurrentRevisionContentChange()
     249    {
     250        if (this._mimeType !== this.currentRevision.mimeType) {
    274251            let oldMIMEType = this._mimeType;
    275             this._mimeType = mimeType;
     252            this._mimeType = this.currentRevision.mimeType;
    276253            this.dispatchEventToListeners(WI.Resource.Event.MIMETypeDidChange, {oldMIMEType});
    277254        }
    278255    }
    279 
    280     // Protected
    281 
    282     requestContentFromBackend()
    283     {
    284         return Promise.resolve({
    285             content: this._localContent,
    286             base64Encoded: this._localContentIsBase64Encoded,
    287         });
    288     }
    289256};
  • trunk/Source/WebInspectorUI/UserInterface/Models/Resource.js

    r250813 r251024  
    423423    createObjectURL()
    424424    {
     425        let revision = this.currentRevision;
     426        let blobContent = revision.blobContent;
     427        if (blobContent)
     428            return URL.createObjectURL(blobContent)
     429
    425430        // If content is not available, fallback to using original URL.
    426431        // The client may try to revoke it, but nothing will happen.
    427         let content = this.content;
    428         if (!content)
    429             return this._url;
    430 
    431         if (content instanceof Blob)
    432             return URL.createObjectURL(content);
    433 
    434         if (typeof content === "string") {
    435             let blob = textToBlob(content, this._mimeType);
    436             return URL.createObjectURL(blob);
    437         }
    438 
    439         return null;
     432        return this._url;
    440433    }
    441434
     
    10661059    }
    10671060
    1068     async createLocalResourceOverride(initialContent)
     1061    async createLocalResourceOverride({initialContent} = {})
    10691062    {
    10701063        console.assert(!this.isLocalResourceOverride);
  • trunk/Source/WebInspectorUI/UserInterface/Models/Script.js

    r249450 r251024  
    118118    get mimeType()
    119119    {
    120         return this._resource.mimeType;
     120        return this._resource ? this._resource.mimeType : "text/javascript";
    121121    }
    122122
  • trunk/Source/WebInspectorUI/UserInterface/Models/SourceCode.js

    r250618 r251024  
    3030        super();
    3131
    32         this._originalRevision = new WI.SourceCodeRevision(this, null, false);
     32        this._originalRevision = new WI.SourceCodeRevision(this);
    3333        this._currentRevision = this._originalRevision;
    3434
     
    232232        // Different backend APIs return one of `content, `body`, `text`, or `scriptSource`.
    233233        let rawContent = parameters.content || parameters.body || parameters.text || parameters.scriptSource;
     234        let rawBase64Encoded = !!parameters.base64Encoded;
    234235        let content = rawContent;
    235236        let error = parameters.error;
     
    237238
    238239        if (parameters.base64Encoded)
    239             content = content ? decodeBase64ToBlob(content, this.mimeType) : "";
     240            content = content ? WI.BlobUtilities.decodeBase64ToBlob(content, this.mimeType) : "";
    240241
    241242        let revision = this.revisionForRequestedContent;
    242243
    243244        this._ignoreRevisionContentDidChangeEvent = true;
    244         revision.content = content || null;
     245        revision.updateRevisionContent(rawContent, {
     246            base64Encoded: rawBase64Encoded,
     247            mimeType: this.mimeType,
     248            blobContent: content instanceof Blob ? content : null,
     249        });
    245250        this._ignoreRevisionContentDidChangeEvent = false;
    246251
     
    250255        // now, and it may become out-dated later on. We should drop content from this promise
    251256        // and require clients to ask for the current contents from the sourceCode in the result.
     257        // That would also avoid confusion around `content` being a Blob and eliminate the work
     258        // of creating the Blob if it is not used.
    252259
    253260        return Promise.resolve({
     
    257264            content,
    258265            rawContent,
    259             rawBase64Encoded: parameters.base64Encoded,
     266            rawBase64Encoded,
    260267        });
    261268    }
  • trunk/Source/WebInspectorUI/UserInterface/Models/SourceCodeRevision.js

    r220119 r251024  
    11/*
    2  * Copyright (C) 2013 Apple Inc. All rights reserved.
     2 * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2626WI.SourceCodeRevision = class SourceCodeRevision extends WI.Revision
    2727{
    28     constructor(sourceCode, content)
     28    constructor(sourceCode, content, base64Encoded, mimeType)
    2929    {
    3030        super();
    3131
    3232        console.assert(sourceCode instanceof WI.SourceCode);
     33        console.assert(content === undefined || typeof content === "string");
     34        console.assert(base64Encoded === undefined || typeof base64Encoded === "boolean");
     35        console.assert(mimeType === undefined || typeof mimeType === "string");
    3336
    3437        this._sourceCode = sourceCode;
     38
    3539        this._content = content || "";
     40        this._base64Encoded = !!base64Encoded;
     41        this._mimeType = mimeType;
     42        this._blobContent = null;
    3643    }
    3744
    3845    // Public
    3946
    40     get sourceCode()
     47    get sourceCode() { return this._sourceCode; }
     48    get content() { return this._content; }
     49    get base64Encoded() { return this._base64Encoded; }
     50    get mimeType() { return this._mimeType; }
     51
     52    get blobContent()
    4153    {
    42         return this._sourceCode;
     54        if (!this._blobContent && this._content)
     55            this._blobContent = WI.BlobUtilities.blobForContent(this._content, this._base64Encoded, this._mimeType);
     56
     57        console.assert(!this._blobContent || this._blobContent instanceof Blob);
     58        return this._blobContent;
    4359    }
    4460
    45     get content()
     61    updateRevisionContent(content, {base64Encoded, mimeType, blobContent} = {})
    4662    {
    47         return this._content;
    48     }
     63        console.assert(content === undefined || typeof content === "string");
     64        this._content = content || "";
    4965
    50     set content(content)
    51     {
    52         content = content || "";
     66        if (base64Encoded !== undefined) {
     67            console.assert(typeof base64Encoded === "boolean");
     68            this._base64Encoded = !!base64Encoded;
     69        }
    5370
    54         if (this._content === content)
    55             return;
     71        if (mimeType !== undefined) {
     72            console.assert(typeof mimeType === "string");
     73            this._mimeType = mimeType;
     74        }
    5675
    57         this._content = content;
     76        console.assert(!blobContent || blobContent instanceof Blob);
     77        this._blobContent = blobContent !== undefined ? blobContent : null;
    5878
    5979        this._sourceCode.revisionContentDidChange(this);
     
    7292    copy()
    7393    {
    74         return new WI.SourceCodeRevision(this._sourceCode, this._content);
     94        return new WI.SourceCodeRevision(this._sourceCode, this._content, this._base64Encoded, this._mimeType);
    7595    }
    7696};
  • trunk/Source/WebInspectorUI/UserInterface/Test.html

    r249831 r251024  
    5555    <script src="Test/TestUtilities.js"></script>
    5656
     57    <script src="Base/BlobUtilities.js"></script>
    5758    <script src="Base/DOMUtilities.js"></script>
    5859    <script src="Base/EventListener.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Views/DropZoneView.css

    r251023 r251024  
    11/*
    2  * Copyright (C) 2013 Apple Inc. All rights reserved.
     2 * Copyright (C) 2019 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2424 */
    2525
    26 .content-view.resource.image {
    27     background-color: hsl(0, 0%, 90%);
     26.drop-zone {
     27    display: none;
     28    position: absolute;
     29    top: 0;
     30    left: 0;
     31    right: 0;
     32    bottom: 0;
     33    z-index: var(--z-index-glass-pane-for-drag);
     34}
    2835
    29     overflow-x: hidden;
    30     overflow-y: auto;
    31 
     36.drop-zone.visible {
    3237    display: flex;
    33 
    3438    justify-content: center;
    3539    align-items: center;
    36 
    37     padding: 15px;
    38 }
    39 
    40 .content-view.resource.image img {
    41     max-width: 100%;
    42 
    43     -webkit-user-select: text;
    44     -webkit-user-drag: auto;
    45 
    46     margin: auto 0;
     40    font-size: 60px;
     41    text-align: center;
     42    color: white;
     43    text-shadow: 0 1px black;
     44    background-color: hsla(0, 0%, 50%, 0.50);
     45    border: 3px dashed hsla(0, 0%, 100%, 0.9);
     46    -webkit-backdrop-filter: blur(10px);
    4747}
    4848
    4949@media (prefers-color-scheme: dark) {
    50     .content-view.resource.image {
    51         background: var(--background-color-content);
     50    .drop-zone.visible {
     51        color: hsl(0, 0%, 80%);
     52        text-shadow: 0 1px hsl(0, 0%, 20%);
     53        background-color: hsla(0, 0%, 30%, 0.5);
     54        border: 3px dashed hsla(0, 0%, 65%, 0.9);
    5255    }
    5356}
  • trunk/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.css

    r242768 r251024  
    2626.content-view.resource.font {
    2727    display: flex;
    28 
     28    flex-direction: column;
    2929    justify-content: center;
    30 
     30    align-items: center;
    3131    overflow-x: hidden;
    3232    overflow-y: auto;
     33}
     34
     35.content-view.resource.font > .drop-zone {
     36    top: calc(var(--navigation-bar-height) - 2px); /* borders */
     37}
     38
     39.content-view.resource.font > .preview-container {
     40    display: flex;
     41    justify-content: center;
     42    align-items: center;
     43    width: 100%;
     44    height: 100%;
    3345}
    3446
     
    4355    for the padding/margin in FontResourceContentView.siteToFit. */
    4456    border: 15px solid transparent;
    45 
    46     margin: auto 0;
    4757}
    4858
  • trunk/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.js

    r220119 r251024  
    3232        this._styleElement = null;
    3333        this._previewElement = null;
     34        this._previewContainer = null;
     35
     36        if (this.showingLocalResourceOverride)
     37            this._dropZoneView = new WI.DropZoneView(this, {text: WI.UIString("Drop Font")});
    3438    }
    3539
    3640    // Public
    37 
    38     get previewElement()
    39     {
    40         return this._previewElement;
    41     }
    4241
    4342    sizeToFit()
     
    5756    {
    5857        this._fontObjectURL = this.resource.createObjectURL();
     58
    5959        if (!this._fontObjectURL) {
    6060            this.showGenericErrorMessage();
     
    6262        }
    6363
    64         const uniqueFontName = "WebInspectorFontPreview" + (++WI.FontResourceContentView._uniqueFontIdentifier);
    65 
    66         var format = "";
    67 
    68         // We need to specify a format when loading SVG fonts to make them work.
    69         if (this.resource.mimeTypeComponents.type === "image/svg+xml")
    70             format = " format(\"svg\")";
    71 
    72         if (this._styleElement && this._styleElement.parentNode)
    73             this._styleElement.parentNode.removeChild(this._styleElement);
    74 
    7564        this.removeLoadingIndicator();
    7665
    77         this._styleElement = document.createElement("style");
    78         this._styleElement.textContent = "@font-face { font-family: \"" + uniqueFontName + "\"; src: url(" + this._fontObjectURL + ")" + format + "; }";
    79 
    80         // The style element will be added when shown later if we are not visible now.
    81         if (this.visible)
    82             document.head.appendChild(this._styleElement);
    83 
    84         this._previewElement = document.createElement("div");
    85         this._previewElement.className = "preview";
    86         this._previewElement.style.fontFamily = uniqueFontName;
    87 
    88         function createMetricElement(className)
    89         {
    90             var metricElement = document.createElement("div");
    91             metricElement.className = "metric " + className;
    92             return metricElement;
    93         }
    94 
    95         var lines = WI.FontResourceContentView.PreviewLines;
    96         for (var i = 0; i < lines.length; ++i) {
    97             var lineElement = document.createElement("div");
    98             lineElement.className = "line";
    99 
    100             lineElement.appendChild(createMetricElement("top"));
    101             lineElement.appendChild(createMetricElement("xheight"));
    102             lineElement.appendChild(createMetricElement("middle"));
    103             lineElement.appendChild(createMetricElement("baseline"));
    104             lineElement.appendChild(createMetricElement("bottom"));
    105 
    106             var contentElement = document.createElement("div");
    107             contentElement.className = "content";
    108             contentElement.textContent = lines[i];
    109             lineElement.appendChild(contentElement);
    110 
    111             this._previewElement.appendChild(lineElement);
    112         }
    113 
    114         this.element.appendChild(this._previewElement);
    115 
    116         this.sizeToFit();
     66        this._previewContainer = this.element.appendChild(document.createElement("div"));
     67        this._previewContainer.className = "preview-container";
     68
     69        this._updatePreviewElement();
     70
     71        if (this._dropZoneView) {
     72            this._dropZoneView.targetElement = this._previewContainer;
     73            this.addSubview(this._dropZoneView);
     74        }
    11775    }
    11876
    11977    shown()
    12078    {
     79        super.shown();
     80
    12181        // Add the style element since it is removed when hidden.
    12282        if (this._styleElement)
     
    12787    {
    12888        // Remove the style element so it will not stick around when this content view is destroyed.
    129         if (this._styleElement && this._styleElement.parentNode)
    130             this._styleElement.parentNode.removeChild(this._styleElement);
     89        if (this._styleElement)
     90            this._styleElement.remove();
     91
     92        super.hidden();
    13193    }
    13294
     
    139101        if (this._fontObjectURL)
    140102            URL.revokeObjectURL(this._fontObjectURL);
     103
     104        super.closed();
    141105    }
    142106
     
    145109    layout()
    146110    {
     111        this.sizeToFit();
     112    }
     113
     114    // DropZoneView delegate
     115
     116    dropZoneShouldAppearForDragEvent(dropZone, event)
     117    {
     118        return event.dataTransfer.types.includes("Files");
     119    }
     120
     121    dropZoneHandleDrop(dropZone, event)
     122    {
     123        let files = event.dataTransfer.files;
     124        let file = files.length === 1 ? files[0] : null;
     125        if (!file) {
     126            InspectorFrontendHost.beep();
     127            return;
     128        }
     129
     130        let fileReader = new FileReader;
     131        fileReader.addEventListener("loadend", (event) => {
     132            let localResourceOverride = WI.networkManager.localResourceOverrideForURL(this.resource.url);
     133            if (!localResourceOverride)
     134                return;
     135
     136            let dataURL = fileReader.result;
     137            let {base64, data, mimeType} = parseDataURL(dataURL);
     138
     139            // In case no mime type was determined, try to derive one from the file extension.
     140            if (!mimeType || mimeType === "text/plain") {
     141                let extension = WI.fileExtensionForFilename(file.name);
     142                if (extension)
     143                    mimeType = WI.mimeTypeForFileExtension(extension);
     144            }
     145
     146            let revision = localResourceOverride.localResource.currentRevision;
     147            revision.updateRevisionContent(data, {base64Encoded: base64, mimeType});
     148
     149            this._fontObjectURL = this.resource.createObjectURL();
     150            this._updatePreviewElement();
     151        });
     152        fileReader.readAsDataURL(file);
     153    }
     154
     155    // Private
     156
     157    _updatePreviewElement()
     158    {
     159        if (this._styleElement)
     160            this._styleElement.remove();
     161        if (this._previewElement)
     162            this._previewElement.remove();
     163
     164        const uniqueFontName = "WebInspectorFontPreview" + (++WI.FontResourceContentView._uniqueFontIdentifier);
     165
     166        let format = "";
     167
     168        // We need to specify a format when loading SVG fonts to make them work.
     169        if (this.resource.mimeTypeComponents.type === "image/svg+xml")
     170            format = " format(\"svg\")";
     171
     172        this._styleElement = document.createElement("style");
     173        this._styleElement.textContent = `@font-face { font-family: "${uniqueFontName}"; src: url(${this._fontObjectURL}) ${format}; }`;
     174
     175        // The style element will be added when shown later if we are not visible now.
     176        if (this.visible)
     177            document.head.appendChild(this._styleElement);
     178
     179        this._previewElement = document.createElement("div");
     180        this._previewElement.className = "preview";
     181        this._previewElement.style.fontFamily = uniqueFontName;
     182
     183        function createMetricElement(className) {
     184            let metricElement = document.createElement("div");
     185            metricElement.className = "metric " + className;
     186            return metricElement;
     187        }
     188
     189        for (let line of WI.FontResourceContentView.PreviewLines) {
     190            let lineElement = document.createElement("div");
     191            lineElement.className = "line";
     192
     193            lineElement.appendChild(createMetricElement("top"));
     194            lineElement.appendChild(createMetricElement("xheight"));
     195            lineElement.appendChild(createMetricElement("middle"));
     196            lineElement.appendChild(createMetricElement("baseline"));
     197            lineElement.appendChild(createMetricElement("bottom"));
     198
     199            let contentElement = document.createElement("div");
     200            contentElement.className = "content";
     201            contentElement.textContent = line;
     202            lineElement.appendChild(contentElement);
     203
     204            this._previewElement.appendChild(lineElement);
     205        }
     206
     207        this._previewContainer.appendChild(this._previewElement);
     208
    147209        this.sizeToFit();
    148210    }
  • trunk/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.css

    r239760 r251024  
    11/*
    2  * Copyright (C) 2013 Apple Inc. All rights reserved.
     2 * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2525
    2626.content-view.resource.image {
     27    display: flex;
     28    flex-direction: column;
     29    justify-content: center;
     30    align-items: center;
    2731    background-color: hsl(0, 0%, 90%);
    28 
    2932    overflow-x: hidden;
    3033    overflow-y: auto;
     34}
    3135
     36.content-view.resource.image > .drop-zone {
     37    top: calc(var(--navigation-bar-height) - 2px); /* borders */
     38}
     39
     40.content-view.resource.image > .img-container {
    3241    display: flex;
    33 
    3442    justify-content: center;
    3543    align-items: center;
    36 
     44    width: 100%;
     45    height: 100%;
    3746    padding: 15px;
    3847}
     
    4049.content-view.resource.image img {
    4150    max-width: 100%;
    42 
     51    max-height: 100%;
    4352    -webkit-user-select: text;
    4453    -webkit-user-drag: auto;
    45 
    46     margin: auto 0;
    4754}
    4855
  • trunk/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.js

    r250814 r251024  
    11/*
    2  * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
     2 * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2828    constructor(resource)
    2929    {
     30        console.assert(resource instanceof WI.Resource);
     31
    3032        super(resource, "image");
    3133
    3234        this._imageElement = null;
     35        this._draggingInternalImageElement = false;
    3336
    3437        const toolTip = WI.UIString("Show transparency grid");
     
    3841        this._showGridButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._showGridButtonClicked, this);
    3942        this._showGridButtonNavigationItem.activated = !!WI.settings.showImageGrid.value;
     43
     44        if (this.showingLocalResourceOverride)
     45            this._dropZoneView = new WI.DropZoneView(this, {text: WI.UIString("Drop Image")});
    4046    }
    4147
     
    4450    get navigationItems()
    4551    {
    46         return [this._showGridButtonNavigationItem];
     52        let items = super.navigationItems;
     53
     54        items.push(this._showGridButtonNavigationItem);
     55
     56        return items;
    4757    }
    4858
     
    6272        }
    6373
    64         this._imageElement = document.createElement("img");
     74        let imageContainer = this.element.appendChild(document.createElement("div"));
     75        imageContainer.className = "img-container";
     76
     77        this._imageElement = imageContainer.appendChild(document.createElement("img"));
    6578        this._imageElement.addEventListener("load", function() { URL.revokeObjectURL(objectURL); });
    6679        this._imageElement.src = objectURL;
     
    6881        this._updateImageGrid();
    6982
    70         this.element.appendChild(this._imageElement);
     83        this._imageElement.addEventListener("dragstart", (event) => {
     84            console.assert(!this._draggingInternalImageElement);
     85            this._draggingInternalImageElement = true;
     86        });
     87        this._imageElement.addEventListener("dragend", (event) => {
     88            console.assert(this._draggingInternalImageElement);
     89            this._draggingInternalImageElement = false;
     90        });
     91
     92        if (this._dropZoneView) {
     93            this._dropZoneView.targetElement = imageContainer;
     94            this.addSubview(this._dropZoneView);
     95        }
    7196    }
    7297
     
    87112
    88113        super.hidden();
     114    }
     115
     116    closed()
     117    {
     118        WI.networkManager.removeEventListener(null, null, this);
     119
     120        super.closed();
     121    }
     122
     123    // DropZoneView delegate
     124
     125    dropZoneShouldAppearForDragEvent(dropZone, event)
     126    {
     127        // Do not appear if the drag is the current image inside this view.
     128        if (this._draggingInternalImageElement)
     129            return false;
     130
     131        // Appear if the drop contains a file.
     132        return event.dataTransfer.types.includes("Files");
     133    }
     134
     135    dropZoneHandleDrop(dropZone, event)
     136    {
     137        let files = event.dataTransfer.files;
     138        let file = files.length === 1 ? files[0] : null;
     139        if (!file) {
     140            InspectorFrontendHost.beep();
     141            return;
     142        }
     143
     144        let fileReader = new FileReader;
     145        fileReader.addEventListener("loadend", (event) => {
     146            let localResourceOverride = WI.networkManager.localResourceOverrideForURL(this.resource.url);
     147            if (!localResourceOverride)
     148                return;
     149
     150            let dataURL = fileReader.result;
     151            this._imageElement.src = dataURL;
     152
     153            let {base64, data, mimeType} = parseDataURL(dataURL);
     154
     155            // In case no mime type was determined, try to derive one from the file extension.
     156            if (!mimeType || mimeType === "text/plain") {
     157                let extension = WI.fileExtensionForFilename(file.name);
     158                if (extension)
     159                    mimeType = WI.mimeTypeForFileExtension(extension);
     160            }
     161
     162            let revision = localResourceOverride.localResource.currentRevision;
     163            revision.updateRevisionContent(data, {base64Encoded: base64, mimeType});
     164        });
     165        fileReader.readAsDataURL(file);
    89166    }
    90167
  • trunk/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverridePopover.js

    r249831 r251024  
    119119            statusCode = "200";
    120120        if (!statusText)
    121             statusText = WI.HTTPUtilities.statusTextForStatusCode(statusCode);
     121            statusText = WI.HTTPUtilities.statusTextForStatusCode(parseInt(statusCode));
    122122
    123123        let popoverContentElement = document.createElement("div");
  • trunk/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverrideTreeElement.js

    r249831 r251024  
    126126        let wasSelected = this.selected;
    127127
     128        let revision = this._localResourceOverride.localResource.currentRevision;
    128129        let newLocalResourceOverride = WI.LocalResourceOverride.create({
    129130            url,
     
    132133            statusText,
    133134            headers,
    134             content: this._localResourceOverride.localResource.localContent,
    135             base64Encoded: this._localResourceOverride.localResource.localContentIsBase64Encoded,
     135            content: revision.content,
     136            base64Encoded: revision.base64Encoded,
    136137        });
    137138
  • trunk/Source/WebInspectorUI/UserInterface/Views/ResourceContentView.js

    r247747 r251024  
    11/*
    2  * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
     2 * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    5858                this.addIssue(issues[i]);
    5959        }
     60
     61        this._showingLocalResourceOverride = false;
     62
     63        if (WI.NetworkManager.supportsLocalResourceOverrides()) {
     64            if (resource.isLocalResourceOverride) {
     65                this._showingLocalResourceOverride = true;
     66
     67                this._localResourceOverrideBannerView = new WI.LocalResourceOverrideLabelView(resource);
     68
     69                this._removeLocalResourceOverrideButtonNavigationItem = new WI.ButtonNavigationItem("remove-local-resource-override", WI.UIString("Remove Local Override"), "Images/NavigationItemTrash.svg", 15, 15);
     70                this._removeLocalResourceOverrideButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleRemoveLocalResourceOverride, this);
     71                this._removeLocalResourceOverrideButtonNavigationItem.enabled = true;
     72                this._removeLocalResourceOverrideButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
     73            } else {
     74                this._localResourceOverrideBannerView = new WI.LocalResourceOverrideWarningView(resource);
     75
     76                this._createLocalResourceOverrideButtonNavigationItem = new WI.ButtonNavigationItem("create-local-resource-override", WI.UIString("Create Local Override"), "Images/NavigationItemNetworkOverride.svg", 13, 14);
     77                this._createLocalResourceOverrideButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleCreateLocalResourceOverride, this);
     78                this._createLocalResourceOverrideButtonNavigationItem.enabled = false; // Enabled when the content is available.
     79                this._createLocalResourceOverrideButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
     80            }
     81
     82            WI.networkManager.addEventListener(WI.NetworkManager.Event.LocalResourceOverrideAdded, this._handleLocalResourceOverrideChanged, this);
     83            WI.networkManager.addEventListener(WI.NetworkManager.Event.LocalResourceOverrideRemoved, this._handleLocalResourceOverrideChanged, this);
     84        }
    6085    }
    6186
    6287    // Public
    6388
    64     get resource()
    65     {
    66         return this._resource;
     89    get resource() { return this._resource; }
     90    get showingLocalResourceOverride() { return this._showingLocalResourceOverride; }
     91
     92    get navigationItems()
     93    {
     94        let items = [];
     95
     96        if (this._removeLocalResourceOverrideButtonNavigationItem)
     97            items.push(this._removeLocalResourceOverrideButtonNavigationItem);
     98        if (this._createLocalResourceOverrideButtonNavigationItem)
     99            items.push(this._createLocalResourceOverrideButtonNavigationItem);
     100
     101        return items;
    67102    }
    68103
     
    80115    {
    81116        throw WI.NotImplementedError.subclassMustOverride();
     117    }
     118
     119    localResourceOverrideInitialContent()
     120    {
     121        // Implemented by subclasses if needed.
     122        return {};
    82123    }
    83124
     
    111152        super.closed();
    112153
     154        if (WI.NetworkManager.supportsLocalResourceOverrides())
     155            WI.networkManager.removeEventListener(null, null, this);
     156
    113157        if (!this.managesOwnIssues)
    114158            WI.consoleManager.removeEventListener(null, null, this);
     
    124168        }
    125169
    126         this.element.removeChildren();
     170        this.removeAllSubviews();
     171
     172        if (this._localResourceOverrideBannerView)
     173            this.addSubview(this._localResourceOverrideBannerView);
    127174    }
    128175
     
    145192        console.assert(parameters.sourceCode === this._resource);
    146193        this.contentAvailable(parameters.sourceCode.content, parameters.base64Encoded);
     194
     195        if (this._createLocalResourceOverrideButtonNavigationItem)
     196            this._createLocalResourceOverrideButtonNavigationItem.enabled = WI.networkManager.canBeOverridden(this._resource);
    147197    }
    148198
     
    169219
    170220        var issue = event.data.issue;
    171         if (!WI.ConsoleManager.issueMatchSourceCode(issue, this.resource))
     221        if (!WI.ConsoleManager.issueMatchSourceCode(issue, this._resource))
    172222            return;
    173223
     
    175225    }
    176226
     227    async _handleCreateLocalResourceOverride(event)
     228    {
     229        let initialContent = this.localResourceOverrideInitialContent();
     230        let localResourceOverride = await this._resource.createLocalResourceOverride(initialContent);
     231        WI.networkManager.addLocalResourceOverride(localResourceOverride);
     232        WI.showLocalResourceOverride(localResourceOverride);
     233    }
     234
     235    _handleRemoveLocalResourceOverride(event)
     236    {
     237        console.assert(this._showingLocalResourceOverride);
     238
     239        let localResourceOverride = WI.networkManager.localResourceOverrideForURL(this._resource.url);
     240        WI.networkManager.removeLocalResourceOverride(localResourceOverride);
     241    }
     242
     243    _handleLocalResourceOverrideChanged(event)
     244    {
     245        if (this._resource.url !== event.data.localResourceOverride.url)
     246            return;
     247
     248        if (this._createLocalResourceOverrideButtonNavigationItem)
     249            this._createLocalResourceOverrideButtonNavigationItem.enabled = WI.networkManager.canBeOverridden(this._resource);
     250    }
     251
    177252    _mouseWasClicked(event)
    178253    {
    179         WI.handlePossibleLinkClick(event, this.resource.parentFrame);
     254        WI.handlePossibleLinkClick(event, this._resource.parentFrame);
    180255    }
    181256};
  • trunk/Source/WebInspectorUI/UserInterface/Views/TextResourceContentView.js

    r250149 r251024  
    5757        WI.settings.enableControlFlowProfiler.addEventListener(WI.Setting.Event.Changed, this._enableControlFlowProfilerSettingChanged, this);
    5858
    59         this._showingLocalResourceOverride = false;
    60 
    61         if (WI.NetworkManager.supportsLocalResourceOverrides()) {
    62             if (resource instanceof WI.Resource && resource.isLocalResourceOverride) {
    63                 this._showingLocalResourceOverride = true;
    64                 this._localResourceOverrideBannerView = new WI.LocalResourceOverrideLabelView(resource);
    65 
    66                 this._removeLocalResourceOverrideButtonNavigationItem = new WI.ButtonNavigationItem("remove-local-resource-override", WI.UIString("Remove Local Override"), "Images/NavigationItemTrash.svg", 15, 15);
    67                 this._removeLocalResourceOverrideButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleRemoveLocalResourceOverride, this);
    68                 this._removeLocalResourceOverrideButtonNavigationItem.enabled = true;
    69                 this._removeLocalResourceOverrideButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
    70             } else {
    71                 this._localResourceOverrideBannerView = new WI.LocalResourceOverrideWarningView(resource);
    72 
    73                 this._createLocalResourceOverrideButtonNavigationItem = new WI.ButtonNavigationItem("create-local-resource-override", WI.UIString("Create Local Override"), "Images/NavigationItemNetworkOverride.svg", 13, 14);
    74                 this._createLocalResourceOverrideButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleCreateLocalResourceOverride, this);
    75                 this._createLocalResourceOverrideButtonNavigationItem.enabled = false; // Enabled when the text editor is populated with content.
    76                 this._createLocalResourceOverrideButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
    77             }
    78         }
    79 
    8059        this._textEditor = new WI.SourceCodeTextEditor(resource);
    8160        this._textEditor.addEventListener(WI.TextEditor.Event.ExecutionLineNumberDidChange, this._executionLineNumberDidChange, this);
     
    9069        WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ProbeSetAdded, this._probeSetsChanged, this);
    9170        WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ProbeSetRemoved, this._probeSetsChanged, this);
    92 
    93         if (WI.NetworkManager.supportsLocalResourceOverrides()) {
    94             WI.networkManager.addEventListener(WI.NetworkManager.Event.LocalResourceOverrideAdded, this._handleLocalResourceOverrideChanged, this);
    95             WI.networkManager.addEventListener(WI.NetworkManager.Event.LocalResourceOverrideRemoved, this._handleLocalResourceOverrideChanged, this);
    96         }
    9771    }
    9872
     
    10175    get navigationItems()
    10276    {
    103         let items = [];
    104 
    105         if (this._removeLocalResourceOverrideButtonNavigationItem)
    106             items.push(this._removeLocalResourceOverrideButtonNavigationItem);
    107         if (this._createLocalResourceOverrideButtonNavigationItem)
    108             items.push(this._createLocalResourceOverrideButtonNavigationItem);
     77        let items = super.navigationItems;
    10978
    11079        items.push(this._prettyPrintButtonNavigationItem);
    11180
    112         if (!this._showingLocalResourceOverride)
     81        if (!this.showingLocalResourceOverride)
    11382            items.push(this._showTypesButtonNavigationItem, this._codeCoverageButtonNavigationItem);
    11483
     
    176145    }
    177146
     147    localResourceOverrideInitialContent()
     148    {
     149        return {initialContent: this._textEditor.string};
     150    }
     151
    178152    get supportsSave()
    179153    {
     
    244218        this.removeLoadingIndicator();
    245219
    246         if (this._localResourceOverrideBannerView)
    247             this.addSubview(this._localResourceOverrideBannerView);
    248 
    249220        this.addSubview(this._textEditor);
    250221    }
     
    252223    _contentDidPopulate(event)
    253224    {
    254         if (this._createLocalResourceOverrideButtonNavigationItem)
    255             this._createLocalResourceOverrideButtonNavigationItem.enabled = WI.networkManager.canBeOverridden(this.resource);
    256 
    257225        this._prettyPrintButtonNavigationItem.enabled = this._textEditor.canBeFormatted();
    258226
     
    262230        this._codeCoverageButtonNavigationItem.enabled = this._textEditor.canShowCoverageHints();
    263231        this._codeCoverageButtonNavigationItem.activated = WI.settings.enableControlFlowProfiler.value;
    264     }
    265 
    266     async _handleCreateLocalResourceOverride(event)
    267     {
    268         let localResourceOverride = await this.resource.createLocalResourceOverride(this._textEditor.string);
    269         WI.networkManager.addLocalResourceOverride(localResourceOverride);
    270         WI.showLocalResourceOverride(localResourceOverride);
    271     }
    272 
    273     _handleRemoveLocalResourceOverride(event)
    274     {
    275         console.assert(this._showingLocalResourceOverride);
    276 
    277         let localResourceOverride = WI.networkManager.localResourceOverrideForURL(this.resource.url);
    278         WI.networkManager.removeLocalResourceOverride(localResourceOverride);
    279232    }
    280233
     
    321274    }
    322275
    323     _handleLocalResourceOverrideChanged(event)
    324     {
    325         if (this.resource.url !== event.data.localResourceOverride.url)
    326             return;
    327 
    328         if (this._createLocalResourceOverrideButtonNavigationItem)
    329             this._createLocalResourceOverrideButtonNavigationItem.enabled = WI.networkManager.canBeOverridden(this.resource);
    330     }
    331 
    332276    _sourceCodeContentDidChange(event)
    333277    {
     
    341285    {
    342286        this._ignoreSourceCodeContentDidChangeEvent = true;
    343         this.resource.currentRevision.content = this._textEditor.string;
     287        this.resource.currentRevision.updateRevisionContent(this._textEditor.string);
    344288        this._ignoreSourceCodeContentDidChangeEvent = false;
    345289    }
     
    375319            return true;
    376320
    377         if (this._showingLocalResourceOverride)
     321        if (this.showingLocalResourceOverride)
    378322            return true;
    379323
Note: See TracChangeset for help on using the changeset viewer.