- a/Source/JavaScriptCore/ChangeLog +12 lines
Lines 1-3 a/Source/JavaScriptCore/ChangeLog_sec1
1
2020-03-20  Devin Rousso  <drousso@apple.com>
2
3
        Web Inspector: support editing cookie key/values from inspector
4
        https://bugs.webkit.org/show_bug.cgi?id=31157
5
        <rdar://problem/19281523>
6
7
        Reviewed by NOBODY (OOPS!).
8
9
        * inspector/protocol/Page.json:
10
        Add a `session` parameter to `Page.Cookie` type and a new `Page.setCookie` command.
11
        Remove the `size` parameter from `Page.Cookie` as this can be calculated in the frontend.
12
1
2020-03-20  Ross Kirsling  <ross.kirsling@sony.com>
13
2020-03-20  Ross Kirsling  <ross.kirsling@sony.com>
2
14
3
        hasObservableSideEffectsForRegExpSplit doesn't check for @@match override
15
        hasObservableSideEffectsForRegExpSplit doesn't check for @@match override
- a/Source/WebCore/ChangeLog +20 lines
Lines 1-3 a/Source/WebCore/ChangeLog_sec1
1
2020-03-20  Devin Rousso  <drousso@apple.com>
2
3
        Web Inspector: support editing cookie key/values from inspector
4
        https://bugs.webkit.org/show_bug.cgi?id=31157
5
        <rdar://problem/19281523>
6
7
        Reviewed by NOBODY (OOPS!).
8
9
        Test: http/tests/inspector/page/setCookie.html
10
11
        * inspector/agents/InspectorPageAgent.h:
12
        * inspector/agents/InspectorPageAgent.cpp:
13
        (WebCore::buildObjectForCookie):
14
        (WebCore::parseCookieObject): Added.
15
        (WebCore::InspectorPageAgent::setCookie): Added.
16
17
        * loader/CookieJar.h:
18
        * loader/CookieJar.cpp:
19
        (WebCore::CookieJar::setRawCookie): Added.
20
1
2020-03-21  Philippe Normand  <pnormand@igalia.com>
21
2020-03-21  Philippe Normand  <pnormand@igalia.com>
2
22
3
        Make the MediaSample::toJSONString method generic
23
        Make the MediaSample::toJSONString method generic
- a/Source/WebInspectorUI/ChangeLog +63 lines
Lines 1-3 a/Source/WebInspectorUI/ChangeLog_sec1
1
2020-03-20  Devin Rousso  <drousso@apple.com>
2
3
        Web Inspector: support editing cookie key/values from inspector
4
        https://bugs.webkit.org/show_bug.cgi?id=31157
5
        <rdar://problem/19281523>
6
7
        Reviewed by NOBODY (OOPS!).
8
9
        * UserInterface/Models/Cookie.js:
10
        (WI.Cookie):
11
        (WI.Cookie.fromPayload):
12
        (WI.Cookie.parseSetCookieResponseHeader):
13
        (WI.Cookie.prototype.get session): Added.
14
        (WI.Cookie.prototype.expirationDate):
15
        (WI.Cookie.prototype.equals): Added.
16
        (WI.Cookie.prototype.toProtocol): Added.
17
        Add `session` value in addition to the existing `expires` value. Create helper methods for
18
        comparing `WI.Cookie` objects and for using the `WI.Cookie` as a `Page.Cookie` type when
19
        invoking protocol commands (right now just `Page.setCookie`).
20
21
        * UserInterface/Views/CookieStorageContentView.js:
22
        (WI.CookieStorageContentView):
23
        (WI.CookieStorageContentView.prototype.get navigationItems):
24
        (WI.CookieStorageContentView.prototype.tableCellContextMenuClicked):
25
        (WI.CookieStorageContentView.prototype.willDismissPopover): Added.
26
        (WI.CookieStorageContentView.prototype.async _willDismissCookiePopover): Added.
27
        (WI.CookieStorageContentView.prototype._handleSetCookieButtonClick): Added.
28
        (WI.CookieStorageContentView.prototype._reloadCookies):
29
        (WI.CookieStorageContentView.prototype._formatCookiePropertyForColumn):
30
        Add a + navigation item that shows a popover for creating a new cookie. When contextmenu
31
        clicking on a table row, add an "Edit" item that shows a popover for creating a new cookie
32
        with the values from the existing cookie, which will "replace" (delete and set) the existing
33
        cookie upon being dismissed.
34
35
        * UserInterface/Views/ResourceCookiesContentView.js:
36
        (WI.ResourceCookiesContentView.prototype.tablePopulateCell):
37
        If only use the `expires` value if `session` is not set.
38
39
        * UserInterface/Views/CookiePopover.js: Added.
40
        (WI.CookiePopover):
41
        (WI.CookiePopover.prototype.get serializedData):
42
        (WI.CookiePopover.prototype.show.createRow):
43
        (WI.CookiePopover.prototype.show.createInputRow):
44
        (WI.CookiePopover.prototype.show):
45
        (WI.CookiePopover.prototype._presentOverTargetElement):
46
        (WI.CookiePopover.prototype._defaultExpires):
47
        (WI.CookiePopover.prototype._parseExpires):
48
        (WI.CookiePopover.prototype._handleInputKeyDown):
49
        * UserInterface/Views/CookiePopover.css: Added.
50
        (.popover .cookie-popover-content):
51
        (.popover .cookie-popover-content > table):
52
        (.popover .cookie-popover-content > table > tr > th):
53
        (.popover .cookie-popover-content > table > tr > td):
54
        (.popover .cookie-popover-content > table > tr > td > input:matches([type="text"], [type="datetime-local"])):
55
        (.popover .cookie-popover-content > table > tr > td > input:matches([type="text"], [type="datetime-local"]).invalid):
56
        (@media (prefers-color-scheme: dark) .popover .cookie-popover-content > table > tr > th):
57
        Show an  `<input>` (or `<select>`) for each configuration option when creating a cookie.
58
        Hide the `<input>` for `expires` if the `<input type="checkbox">` for `session` is checked.
59
        Indicate when the value in the `<input>` for `expires` is not a valid date.
60
61
        * UserInterface/Main.html:
62
        * Localizations/en.lproj/localizedStrings.js:
63
1
2020-03-21  Devin Rousso  <drousso@apple.com>
64
2020-03-21  Devin Rousso  <drousso@apple.com>
2
65
3
        Web Inspector: REGRESSION(r257380, r257759): focusing the inspected page when docked dims most of the interface
66
        Web Inspector: REGRESSION(r257380, r257759): focusing the inspected page when docked dims most of the interface
- a/Source/WebKit/ChangeLog +17 lines
Lines 1-3 a/Source/WebKit/ChangeLog_sec1
1
2020-03-20  Devin Rousso  <drousso@apple.com>
2
3
        Web Inspector: support editing cookie key/values from inspector
4
        https://bugs.webkit.org/show_bug.cgi?id=31157
5
        <rdar://problem/19281523>
6
7
        Reviewed by NOBODY (OOPS!).
8
9
        * WebProcess/WebPage/WebCookieJar.h:
10
        * WebProcess/WebPage/WebCookieJar.cpp:
11
        (WebKit::WebCookieJar::setRawCookie):
12
13
        * NetworkProcess/NetworkConnectionToWebProcess.messages.in:
14
        * NetworkProcess/NetworkConnectionToWebProcess.h:
15
        * NetworkProcess/NetworkConnectionToWebProcess.cpp:
16
        (WebKit::NetworkConnectionToWebProcess::setRawCookie): Added.
17
1
2020-03-20  Wenson Hsieh  <wenson_hsieh@apple.com>
18
2020-03-20  Wenson Hsieh  <wenson_hsieh@apple.com>
2
19
3
        [iPadOS] Yahoo! search results are sometimes zoomed in a little
20
        [iPadOS] Yahoo! search results are sometimes zoomed in a little
- a/Source/JavaScriptCore/inspector/protocol/Page.json -3 / +9 lines
Lines 104-113 a/Source/JavaScriptCore/inspector/protocol/Page.json_sec1
104
                { "name": "domain", "type": "string", "description": "Cookie domain." },
104
                { "name": "domain", "type": "string", "description": "Cookie domain." },
105
                { "name": "path", "type": "string", "description": "Cookie path." },
105
                { "name": "path", "type": "string", "description": "Cookie path." },
106
                { "name": "expires", "type": "number", "description": "Cookie expires." },
106
                { "name": "expires", "type": "number", "description": "Cookie expires." },
107
                { "name": "size", "type": "integer", "description": "Cookie size." },
107
                { "name": "session", "type": "boolean", "description": "True in case of session cookie." },
108
                { "name": "httpOnly", "type": "boolean", "description": "True if cookie is http-only." },
108
                { "name": "httpOnly", "type": "boolean", "description": "True if cookie is http-only." },
109
                { "name": "secure", "type": "boolean", "description": "True if cookie is secure." },
109
                { "name": "secure", "type": "boolean", "description": "True if cookie is secure." },
110
                { "name": "session", "type": "boolean", "description": "True in case of session cookie." },
111
                { "name": "sameSite", "$ref": "CookieSameSitePolicy", "description": "Cookie Same-Site policy." }
110
                { "name": "sameSite", "$ref": "CookieSameSitePolicy", "description": "Cookie Same-Site policy." }
112
            ]
111
            ]
113
        }
112
        }
Lines 158-166 a/Source/JavaScriptCore/inspector/protocol/Page.json_sec2
158
                { "name": "cookies", "type": "array", "items": { "$ref": "Cookie"}, "description": "Array of cookie objects." }
157
                { "name": "cookies", "type": "array", "items": { "$ref": "Cookie"}, "description": "Array of cookie objects." }
159
            ]
158
            ]
160
        },
159
        },
160
        {
161
            "name": "setCookie",
162
            "description": "Sets a new browser cookie with the given name, domain, and path.",
163
            "parameters": [
164
                { "name": "cookie", "$ref": "Cookie" }
165
            ]
166
        },
161
        {
167
        {
162
            "name": "deleteCookie",
168
            "name": "deleteCookie",
163
            "description": "Deletes browser cookie with given name, domain and path.",
169
            "description": "Deletes browser cookie with given name, domain, and path.",
164
            "parameters": [
170
            "parameters": [
165
                { "name": "cookieName", "type": "string", "description": "Name of the cookie to remove." },
171
                { "name": "cookieName", "type": "string", "description": "Name of the cookie to remove." },
166
                { "name": "url", "type": "string", "description": "URL to match cookie domain and path." }
172
                { "name": "url", "type": "string", "description": "URL to match cookie domain and path." }
- a/Source/WebCore/inspector/agents/InspectorPageAgent.cpp -2 / +85 lines
Lines 489-498 static Ref<Inspector::Protocol::Page::Cookie> buildObjectForCookie(const Cookie& a/Source/WebCore/inspector/agents/InspectorPageAgent.cpp_sec1
489
        .setDomain(cookie.domain)
489
        .setDomain(cookie.domain)
490
        .setPath(cookie.path)
490
        .setPath(cookie.path)
491
        .setExpires(cookie.expires.valueOr(0))
491
        .setExpires(cookie.expires.valueOr(0))
492
        .setSize((cookie.name.length() + cookie.value.length()))
492
        .setSession(cookie.session)
493
        .setHttpOnly(cookie.httpOnly)
493
        .setHttpOnly(cookie.httpOnly)
494
        .setSecure(cookie.secure)
494
        .setSecure(cookie.secure)
495
        .setSession(cookie.session)
496
        .setSameSite(cookieSameSitePolicyJSON(cookie.sameSite))
495
        .setSameSite(cookieSameSitePolicyJSON(cookie.sameSite))
497
        .release();
496
        .release();
498
}
497
}
Lines 560-565 void InspectorPageAgent::getCookies(ErrorString&, RefPtr<JSON::ArrayOf<Inspector a/Source/WebCore/inspector/agents/InspectorPageAgent.cpp_sec2
560
        cookies = JSON::ArrayOf<Inspector::Protocol::Page::Cookie>::create();
559
        cookies = JSON::ArrayOf<Inspector::Protocol::Page::Cookie>::create();
561
}
560
}
562
561
562
static Optional<Cookie> parseCookieObject(ErrorString& errorString, const JSON::Object& cookieObject)
563
{
564
    Cookie cookie;
565
566
    if (!cookieObject.getString("name"_s, cookie.name)) {
567
        errorString = "Invalid value for key name in given cookie";
568
        return WTF::nullopt;
569
    }
570
571
    if (!cookieObject.getString("value"_s, cookie.value)) {
572
        errorString = "Invalid value for key value in given cookie";
573
        return WTF::nullopt;
574
    }
575
576
    if (!cookieObject.getString("domain"_s, cookie.domain)) {
577
        errorString = "Invalid value for key domain in given cookie";
578
        return WTF::nullopt;
579
    }
580
581
    if (!cookieObject.getString("path"_s, cookie.path)) {
582
        errorString = "Invalid value for key path in given cookie";
583
        return WTF::nullopt;
584
    }
585
586
    if (!cookieObject.getBoolean("httpOnly"_s, cookie.httpOnly)) {
587
        errorString = "Invalid value for key httpOnly in given cookie";
588
        return WTF::nullopt;
589
    }
590
591
    if (!cookieObject.getBoolean("secure"_s, cookie.secure)) {
592
        errorString = "Invalid value for key secure in given cookie";
593
        return WTF::nullopt;
594
    }
595
596
    bool validSession = cookieObject.getBoolean("session"_s, cookie.session);
597
    cookie.expires = cookieObject.getNumber<double>("expires"_s);
598
    if (!validSession && !cookie.expires) {
599
        errorString = "Invalid value for key expires in given cookie";
600
        return WTF::nullopt;
601
    }
602
603
    String sameSiteString;
604
    if (!cookieObject.getString("sameSite"_s, sameSiteString)) {
605
        errorString = "Invalid value for key sameSite in given cookie";
606
        return WTF::nullopt;
607
    }
608
609
    auto sameSite = Inspector::Protocol::InspectorHelpers::parseEnumValueFromString<Inspector::Protocol::Page::CookieSameSitePolicy>(sameSiteString);
610
    if (!sameSite) {
611
        errorString = "Invalid value for key sameSite in given cookie";
612
        return WTF::nullopt;
613
    }
614
615
    switch (sameSite.value()) {
616
    case Inspector::Protocol::Page::CookieSameSitePolicy::None:
617
        cookie.sameSite = Cookie::SameSitePolicy::None;
618
619
        break;
620
    case Inspector::Protocol::Page::CookieSameSitePolicy::Lax:
621
        cookie.sameSite = Cookie::SameSitePolicy::Lax;
622
623
        break;
624
    case Inspector::Protocol::Page::CookieSameSitePolicy::Strict:
625
        cookie.sameSite = Cookie::SameSitePolicy::Strict;
626
        break;
627
    }
628
629
    return cookie;
630
}
631
632
void InspectorPageAgent::setCookie(ErrorString& errorString, const JSON::Object& cookieObject)
633
{
634
    auto cookie = parseCookieObject(errorString, cookieObject);
635
    if (!cookie)
636
        return;
637
638
    for (auto* frame = &m_inspectedPage.mainFrame(); frame; frame = frame->tree().traverseNext()) {
639
        if (auto* document = frame->document()) {
640
            if (auto* page = document->page())
641
                page->cookieJar().setRawCookie(*document, cookie.value());
642
        }
643
    }
644
}
645
563
void InspectorPageAgent::deleteCookie(ErrorString&, const String& cookieName, const String& url)
646
void InspectorPageAgent::deleteCookie(ErrorString&, const String& cookieName, const String& url)
564
{
647
{
565
    URL parsedURL({ }, url);
648
    URL parsedURL({ }, url);
- a/Source/WebCore/inspector/agents/InspectorPageAgent.h +1 lines
Lines 101-106 public: a/Source/WebCore/inspector/agents/InspectorPageAgent.h_sec1
101
    void overrideUserAgent(ErrorString&, const String* value) override;
101
    void overrideUserAgent(ErrorString&, const String* value) override;
102
    void overrideSetting(ErrorString&, const String& setting, const bool* value) override;
102
    void overrideSetting(ErrorString&, const String& setting, const bool* value) override;
103
    void getCookies(ErrorString&, RefPtr<JSON::ArrayOf<Inspector::Protocol::Page::Cookie>>& cookies) override;
103
    void getCookies(ErrorString&, RefPtr<JSON::ArrayOf<Inspector::Protocol::Page::Cookie>>& cookies) override;
104
    void setCookie(ErrorString&, const JSON::Object& cookieObject) override;
104
    void deleteCookie(ErrorString&, const String& cookieName, const String& url) override;
105
    void deleteCookie(ErrorString&, const String& cookieName, const String& url) override;
105
    void getResourceTree(ErrorString&, RefPtr<Inspector::Protocol::Page::FrameResourceTree>&) override;
106
    void getResourceTree(ErrorString&, RefPtr<Inspector::Protocol::Page::FrameResourceTree>&) override;
106
    void getResourceContent(ErrorString&, const String& frameId, const String& url, String* content, bool* base64Encoded) override;
107
    void getResourceContent(ErrorString&, const String& frameId, const String& url, String* content, bool* base64Encoded) override;
- a/Source/WebCore/loader/CookieJar.cpp +8 lines
Lines 171-176 bool CookieJar::getRawCookies(const Document& document, const URL& url, Vector<C a/Source/WebCore/loader/CookieJar.cpp_sec1
171
    return false;
171
    return false;
172
}
172
}
173
173
174
void CookieJar::setRawCookie(const Document&, const Cookie& cookie)
175
{
176
    if (auto* session = m_storageSessionProvider->storageSession())
177
        session->setCookie(cookie);
178
    else
179
        ASSERT_NOT_REACHED();
180
}
181
174
void CookieJar::deleteCookie(const Document&, const URL& url, const String& cookieName)
182
void CookieJar::deleteCookie(const Document&, const URL& url, const String& cookieName)
175
{
183
{
176
    if (auto* session = m_storageSessionProvider->storageSession())
184
    if (auto* session = m_storageSessionProvider->storageSession())
- a/Source/WebCore/loader/CookieJar.h +1 lines
Lines 58-63 public: a/Source/WebCore/loader/CookieJar.h_sec1
58
    virtual bool cookiesEnabled(const Document&) const;
58
    virtual bool cookiesEnabled(const Document&) const;
59
    virtual std::pair<String, SecureCookiesAccessed> cookieRequestHeaderFieldValue(const URL& firstParty, const SameSiteInfo&, const URL&, Optional<FrameIdentifier>, Optional<PageIdentifier>, IncludeSecureCookies) const;
59
    virtual std::pair<String, SecureCookiesAccessed> cookieRequestHeaderFieldValue(const URL& firstParty, const SameSiteInfo&, const URL&, Optional<FrameIdentifier>, Optional<PageIdentifier>, IncludeSecureCookies) const;
60
    virtual bool getRawCookies(const Document&, const URL&, Vector<Cookie>&) const;
60
    virtual bool getRawCookies(const Document&, const URL&, Vector<Cookie>&) const;
61
    virtual void setRawCookie(const Document&, const Cookie&);
61
    virtual void deleteCookie(const Document&, const URL&, const String& cookieName);
62
    virtual void deleteCookie(const Document&, const URL&, const String& cookieName);
62
63
63
    // Cookie Cache.
64
    // Cookie Cache.
- a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js +1 lines
Lines 99-104 localizedStrings["Add %s Rule"] = "Add %s Rule"; a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js_sec1
99
localizedStrings["Add Action"] = "Add Action";
99
localizedStrings["Add Action"] = "Add Action";
100
localizedStrings["Add Breakpoint"] = "Add Breakpoint";
100
localizedStrings["Add Breakpoint"] = "Add Breakpoint";
101
localizedStrings["Add Breakpoints"] = "Add Breakpoints";
101
localizedStrings["Add Breakpoints"] = "Add Breakpoints";
102
localizedStrings["Add Cookie"] = "Add Cookie";
102
localizedStrings["Add Header"] = "Add Header";
103
localizedStrings["Add Header"] = "Add Header";
103
localizedStrings["Add New"] = "Add New";
104
localizedStrings["Add New"] = "Add New";
104
localizedStrings["Add New Class"] = "Add New Class";
105
localizedStrings["Add New Class"] = "Add New Class";
- a/Source/WebInspectorUI/UserInterface/Main.html +2 lines
Lines 79-84 a/Source/WebInspectorUI/UserInterface/Main.html_sec1
79
    <link rel="stylesheet" href="Views/ContentBrowserTabContentView.css">
79
    <link rel="stylesheet" href="Views/ContentBrowserTabContentView.css">
80
    <link rel="stylesheet" href="Views/ContentView.css">
80
    <link rel="stylesheet" href="Views/ContentView.css">
81
    <link rel="stylesheet" href="Views/ContentViewContainer.css">
81
    <link rel="stylesheet" href="Views/ContentViewContainer.css">
82
    <link rel="stylesheet" href="Views/CookiePopover.css">
82
    <link rel="stylesheet" href="Views/CookieStorageContentView.css">
83
    <link rel="stylesheet" href="Views/CookieStorageContentView.css">
83
    <link rel="stylesheet" href="Views/DOMBreakpointTreeElement.css">
84
    <link rel="stylesheet" href="Views/DOMBreakpointTreeElement.css">
84
    <link rel="stylesheet" href="Views/DOMEventsBreakdownView.css">
85
    <link rel="stylesheet" href="Views/DOMEventsBreakdownView.css">
Lines 642-647 a/Source/WebInspectorUI/UserInterface/Main.html_sec2
642
    <script src="Views/ContentViewContainer.js"></script>
643
    <script src="Views/ContentViewContainer.js"></script>
643
    <script src="Views/ContextMenu.js"></script>
644
    <script src="Views/ContextMenu.js"></script>
644
    <script src="Views/ContextMenuUtilities.js"></script>
645
    <script src="Views/ContextMenuUtilities.js"></script>
646
    <script src="Views/CookiePopover.js"></script>
645
    <script src="Views/CookieStorageContentView.js"></script>
647
    <script src="Views/CookieStorageContentView.js"></script>
646
    <script src="Views/CookieStorageTreeElement.js"></script>
648
    <script src="Views/CookieStorageTreeElement.js"></script>
647
    <script src="Views/DOMBreakpointTreeElement.js"></script>
649
    <script src="Views/DOMBreakpointTreeElement.js"></script>
- a/Source/WebInspectorUI/UserInterface/Models/Cookie.js -6 / +66 lines
Lines 25-53 a/Source/WebInspectorUI/UserInterface/Models/Cookie.js_sec1
25
25
26
WI.Cookie = class Cookie
26
WI.Cookie = class Cookie
27
{
27
{
28
    constructor(type, name, value, {header, expires, maxAge, path, domain, secure, httpOnly, sameSite, size} = {})
28
    constructor(type, name, value, {header, expires, session, maxAge, path, domain, secure, httpOnly, sameSite} = {})
29
    {
29
    {
30
        console.assert(Object.values(WI.Cookie.Type).includes(type));
30
        console.assert(Object.values(WI.Cookie.Type).includes(type));
31
        console.assert(typeof name === "string");
31
        console.assert(typeof name === "string");
32
        console.assert(typeof value === "string");
32
        console.assert(typeof value === "string");
33
        console.assert(!header || typeof header === "string");
33
        console.assert(!header || typeof header === "string");
34
        console.assert(!expires || expires instanceof Date);
34
        console.assert(!expires || expires instanceof Date);
35
        console.assert(!session || typeof session === "boolean");
35
        console.assert(!maxAge || typeof maxAge === "number");
36
        console.assert(!maxAge || typeof maxAge === "number");
36
        console.assert(!path || typeof path === "string");
37
        console.assert(!path || typeof path === "string");
37
        console.assert(!domain || typeof domain === "string");
38
        console.assert(!domain || typeof domain === "string");
38
        console.assert(!secure || typeof secure === "boolean");
39
        console.assert(!secure || typeof secure === "boolean");
39
        console.assert(!httpOnly || typeof httpOnly === "boolean");
40
        console.assert(!httpOnly || typeof httpOnly === "boolean");
40
        console.assert(!sameSite || Object.values(WI.Cookie.SameSiteType).includes(sameSite));
41
        console.assert(!sameSite || Object.values(WI.Cookie.SameSiteType).includes(sameSite));
41
        console.assert(!size || typeof size === "number");
42
42
43
        this._type = type;
43
        this._type = type;
44
        this._name = name;
44
        this._name = name;
45
        this._value = value;
45
        this._value = value;
46
        this._size = size || this._name.length + this._value.length;
46
        this._size = this._name.length + this._value.length;
47
47
48
        if (this._type === WI.Cookie.Type.Response) {
48
        if (this._type === WI.Cookie.Type.Response) {
49
            this._header = header || "";
49
            this._header = header || "";
50
            this._expires = expires || null;
50
            this._expires = (!session && expires) || null;
51
            this._session = session || false;
51
            this._maxAge = maxAge || null;
52
            this._maxAge = maxAge || null;
52
            this._path = path || null;
53
            this._path = path || null;
53
            this._domain = domain || null;
54
            this._domain = domain || null;
Lines 62-68 WI.Cookie = class Cookie a/Source/WebInspectorUI/UserInterface/Models/Cookie.js_sec2
62
    static fromPayload(payload)
63
    static fromPayload(payload)
63
    {
64
    {
64
        let {name, value, ...options} = payload;
65
        let {name, value, ...options} = payload;
65
        options.expires = options.expires ? new Date(options.expires) : null;
66
        options.expires = options.expires ? new Date(options.expires.maxDecimals(-3)) : null;
66
67
67
        return new WI.Cookie(WI.Cookie.Type.Response, name, value, options);
68
        return new WI.Cookie(WI.Cookie.Type.Response, name, value, options);
68
    }
69
    }
Lines 148-153 WI.Cookie = class Cookie a/Source/WebInspectorUI/UserInterface/Models/Cookie.js_sec3
148
149
149
        let {name, value} = nameValueMatch.groups;
150
        let {name, value} = nameValueMatch.groups;
150
        let expires = null;
151
        let expires = null;
152
        let session = false;
151
        let maxAge = null;
153
        let maxAge = null;
152
        let path = null;
154
        let path = null;
153
        let domain = null;
155
        let domain = null;
Lines 212-218 WI.Cookie = class Cookie a/Source/WebInspectorUI/UserInterface/Models/Cookie.js_sec4
212
            }
214
            }
213
        }
215
        }
214
216
215
        return new WI.Cookie(WI.Cookie.Type.Response, name, value, {header, expires, maxAge, path, domain, secure, httpOnly, sameSite});
217
        if (!expires)
218
            session = true;
219
220
        return new WI.Cookie(WI.Cookie.Type.Response, name, value, {header, expires, session, maxAge, path, domain, secure, httpOnly, sameSite});
216
    }
221
    }
217
222
218
    // Public
223
    // Public
Lines 222-227 WI.Cookie = class Cookie a/Source/WebInspectorUI/UserInterface/Models/Cookie.js_sec5
222
    get value() { return this._value; }
227
    get value() { return this._value; }
223
    get header() { return this._header; }
228
    get header() { return this._header; }
224
    get expires() { return this._expires; }
229
    get expires() { return this._expires; }
230
    get session() { return this._session; }
225
    get maxAge() { return this._maxAge; }
231
    get maxAge() { return this._maxAge; }
226
    get path() { return this._path; }
232
    get path() { return this._path; }
227
    get domain() { return this._domain; }
233
    get domain() { return this._domain; }
Lines 240-245 WI.Cookie = class Cookie a/Source/WebInspectorUI/UserInterface/Models/Cookie.js_sec6
240
246
241
    expirationDate(requestSentDate)
247
    expirationDate(requestSentDate)
242
    {
248
    {
249
        if (this._session)
250
            return null;
251
243
        if (this._maxAge) {
252
        if (this._maxAge) {
244
            let startDate = requestSentDate || new Date;
253
            let startDate = requestSentDate || new Date;
245
            return new Date(startDate.getTime() + (this._maxAge * 1000));
254
            return new Date(startDate.getTime() + (this._maxAge * 1000));
Lines 247-252 WI.Cookie = class Cookie a/Source/WebInspectorUI/UserInterface/Models/Cookie.js_sec7
247
256
248
        return this._expires;
257
        return this._expires;
249
    }
258
    }
259
260
    equals(other)
261
    {
262
        return this._type === other.type
263
            && this._name === other.name
264
            && this._value === other.value
265
            && this._header === other.header
266
            && this._expires?.getTime() === other.expires?.getTime()
267
            && this._session === other.session
268
            && this._maxAge === other.maxAge
269
            && this._path === other.path
270
            && this._domain === other.domain
271
            && this._secure === other.secure
272
            && this._httpOnly === other.httpOnly
273
            && this._sameSite === other.sameSite;
274
    }
275
276
    toProtocol()
277
    {
278
        if (typeof this._name !== "string")
279
            return null;
280
281
        if (typeof this._value !== "string")
282
            return null;
283
284
        if (typeof this._domain !== "string")
285
            return null;
286
287
        if (typeof this._path !== "string")
288
            return null;
289
290
        if (!this._session && !this._expires)
291
            return null;
292
293
        if (!Object.values(WI.Cookie.SameSiteType).includes(this._sameSite))
294
            return null;
295
296
        let json = {
297
            name: this._name,
298
            value: this._value,
299
            domain: this._domain,
300
            path: this._path,
301
            expires: this._expires?.getTime(),
302
            session: this._session,
303
            httpOnly: !!this._httpOnly,
304
            secure: !!this._secure,
305
            sameSite: this._sameSite,
306
        };
307
308
        return json;
309
    }
250
};
310
};
251
311
252
WI.Cookie.Type = {
312
WI.Cookie.Type = {
- a/Source/WebInspectorUI/UserInterface/Views/CookiePopover.css +67 lines
Line 0 a/Source/WebInspectorUI/UserInterface/Views/CookiePopover.css_sec1
1
/*
2
 * Copyright (C) 2020 Apple Inc. All rights reserved.
3
 *
4
 * Redistribution and use in source and binary forms, with or without
5
 * modification, are permitted provided that the following conditions
6
 * are met:
7
 * 1. Redistributions of source code must retain the above copyright
8
 *    notice, this list of conditions and the following disclaimer.
9
 * 2. Redistributions in binary form must reproduce the above copyright
10
 *    notice, this list of conditions and the following disclaimer in the
11
 *    documentation and/or other materials provided with the distribution.
12
 *
13
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23
 * THE POSSIBILITY OF SUCH DAMAGE.
24
 */
25
26
.popover .cookie-popover-content {
27
    max-width: 420px;
28
    padding: 4px 8px;
29
}
30
31
.popover .cookie-popover-content > table {
32
    width: 100%;
33
}
34
35
.popover .cookie-popover-content > table > tr > th {
36
    font-weight: bold;
37
    text-align: end;
38
    line-height: 23px;
39
    vertical-align: top;
40
    white-space: nowrap;
41
    color: hsl(0, 0%, 34%);
42
}
43
44
.popover .cookie-popover-content > table > tr > td {
45
    -webkit-padding-start: 4px;
46
}
47
48
/* FIXME: <https://webkit.org/b/209389> Web Inspector: use native datetime-local picker for changing `expires` value in cookie popover */
49
50
.popover .cookie-popover-content > table > tr > td > input:matches([type="text"], [type="datetime-local"]) {
51
    width: 100%;
52
    padding: 3px 4px 2px;
53
    font-family: Menlo, monospace;
54
    background-color: var(--background-color-code);
55
    border: 1px solid var(--text-color-quaternary);
56
    -webkit-appearance: none;
57
}
58
59
.popover .cookie-popover-content > table > tr > td > input:matches([type="text"], [type="datetime-local"]).invalid {
60
    color: var(--error-text-color);
61
}
62
63
@media (prefers-color-scheme: dark) {
64
    .popover .cookie-popover-content > table > tr > th {
65
        color: var(--text-color-secondary);
66
    }
67
}
- a/Source/WebInspectorUI/UserInterface/Views/CookiePopover.js +256 lines
Line 0 a/Source/WebInspectorUI/UserInterface/Views/CookiePopover.js_sec1
1
/*
2
 * Copyright (C) 2020 Apple Inc. All rights reserved.
3
 *
4
 * Redistribution and use in source and binary forms, with or without
5
 * modification, are permitted provided that the following conditions
6
 * are met:
7
 * 1. Redistributions of source code must retain the above copyright
8
 *    notice, this list of conditions and the following disclaimer.
9
 * 2. Redistributions in binary form must reproduce the above copyright
10
 *    notice, this list of conditions and the following disclaimer in the
11
 *    documentation and/or other materials provided with the distribution.
12
 *
13
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23
 * THE POSSIBILITY OF SUCH DAMAGE.
24
 */
25
26
WI.CookiePopover = class CookiePopover extends WI.Popover
27
{
28
    constructor(delegate)
29
    {
30
        super(delegate);
31
32
        this._nameInputElement = null;
33
        this._valueInputElement = null;
34
        this._domainInputElement = null;
35
        this._pathInputElement = null;
36
        this._sessionCheckboxElement = null;
37
        this._expiresInputElement = null;
38
        this._httpOnlyCheckboxElement = null;
39
        this._secureCheckboxElement = null;
40
        this._sameSiteSelectElement = null;
41
42
        this._serializedDataWhenShown = null;
43
44
        this.windowResizeHandler = this._presentOverTargetElement.bind(this);
45
    }
46
47
    // Public
48
49
    get serializedData()
50
    {
51
        if (!this._targetElement)
52
            return null;
53
54
        let name = this._nameInputElement.value || this._nameInputElement.placeholder;
55
        if (!name)
56
            return null;
57
58
        let value = this._valueInputElement.value || this._valueInputElement.placeholder;
59
        if (!value)
60
            return null;
61
62
        let domain = this._domainInputElement.value || this._domainInputElement.placeholder;
63
        if (!domain)
64
            return null;
65
66
        let path = this._pathInputElement.value || this._pathInputElement.placeholder;
67
        if (!path)
68
            return null;
69
70
        let session = this._sessionCheckboxElement.checked;
71
        let expires = this._parseExpires();
72
        if (!session && isNaN(expires))
73
            return null;
74
75
        // If a full URL is entered in the domain input, parse it to get just the domain.
76
        try {
77
            let url = new URL(domain);
78
            domain = url.hostname;
79
        } catch { }
80
81
        if (!path.startsWith("/"))
82
            path = "/" + path;
83
84
        let data = {
85
            name,
86
            value,
87
            domain,
88
            path,
89
            httpOnly: this._httpOnlyCheckboxElement.checked,
90
            secure: this._secureCheckboxElement.checked,
91
            sameSite: this._sameSiteSelectElement.value,
92
        };
93
94
        if (session)
95
            data.session = true;
96
        else
97
            data.expires = expires;
98
99
        if (JSON.stringify(data) === JSON.stringify(this._serializedDataWhenShown))
100
            return null;
101
102
        return data;
103
    }
104
105
    show(cookie, targetElement, preferredEdges)
106
    {
107
        console.assert(!cookie || cookie instanceof WI.Cookie, cookie);
108
        console.assert(targetElement instanceof Element, targetElement);
109
        console.assert(Array.isArray(preferredEdges), preferredEdges);
110
111
        this._targetElement = targetElement;
112
        this._preferredEdges = preferredEdges;
113
114
        let data = {};
115
        if (cookie) {
116
            data.name = cookie.name;
117
            data.value = cookie.value;
118
            data.domain = cookie.domain;
119
            data.path = cookie.path;
120
            data.expires = (cookie.expires || this._defaultExpires()).toLocaleString();
121
            data.session = cookie.session;
122
            data.httpOnly = cookie.httpOnly;
123
            data.secure = cookie.secure;
124
            data.sameSite = cookie.sameSite;
125
        } else {
126
            let urlComponents = WI.networkManager.mainFrame.mainResource.urlComponents;
127
            data.name = WI.unlocalizedString("name");
128
            data.value = WI.unlocalizedString("value");
129
            data.domain = urlComponents.host;
130
            data.path = urlComponents.path;
131
            data.expires = this._defaultExpires().toLocaleString();
132
            data.session = true;
133
            data.httpOnly = false;
134
            data.secure = false;
135
            data.sameSite = WI.Cookie.SameSiteType.None;
136
        }
137
138
        let popoverContentElement = document.createElement("div");
139
        popoverContentElement.className = "cookie-popover-content";
140
141
        let tableElement = popoverContentElement.appendChild(document.createElement("table"));
142
143
        function createRow(id, label, editorElement) {
144
            id = `cookie-popover-${id}-editor`;
145
146
            let rowElement = tableElement.appendChild(document.createElement("tr"));
147
148
            let headerElement = rowElement.appendChild(document.createElement("th"));
149
150
            let labelElement = headerElement.appendChild(document.createElement("label"));
151
            labelElement.setAttribute("for", id);
152
            labelElement.textContent = label;
153
154
            let dataElement = rowElement.appendChild(document.createElement("td"));
155
156
            editorElement.id = id;
157
            dataElement.appendChild(editorElement);
158
159
            return {rowElement};
160
        }
161
162
        let boundHandleInputKeyDown = this._handleInputKeyDown.bind(this);
163
164
        function createInputRow(id, label, type, value) {
165
            let inputElement = document.createElement("input");
166
            inputElement.type = type;
167
168
            if (type === "checkbox")
169
                inputElement.checked = value;
170
            else {
171
                if (cookie)
172
                    inputElement.value = value;
173
                inputElement.placeholder = value;
174
                inputElement.addEventListener("keydown", boundHandleInputKeyDown);
175
            }
176
177
            let rowElement = createRow(id, label, inputElement).rowElement;
178
179
            return {inputElement, rowElement};
180
        }
181
182
        this._nameInputElement = createInputRow("name", WI.UIString("Name"), "text", data.name).inputElement;
183
184
        this._valueInputElement = createInputRow("value", WI.UIString("Value"), "text", data.value).inputElement;
185
186
        this._domainInputElement = createInputRow("domain", WI.unlocalizedString("Domain"), "text", data.domain).inputElement;
187
188
        this._pathInputElement = createInputRow("path", WI.unlocalizedString("Path"), "text", data.path).inputElement;
189
190
        this._sessionCheckboxElement = createInputRow("session", WI.unlocalizedString("Session"), "checkbox", data.session).inputElement;
191
192
        let expiresInputRow = createInputRow("expires", WI.unlocalizedString("Expires"), "datetime-local", data.expires);
193
        this._expiresInputElement = expiresInputRow.inputElement;
194
        this._expiresInputElement.addEventListener("input", (event) => {
195
            this._expiresInputElement.classList.toggle("invalid", isNaN(this._parseExpires()));
196
        });
197
198
        this._httpOnlyCheckboxElement = createInputRow("http-only", WI.unlocalizedString("HttpOnly"), "checkbox", data.httpOnly).inputElement;
199
200
        this._secureCheckboxElement = createInputRow("secure", WI.unlocalizedString("Secure"), "checkbox", data.secure).inputElement;
201
202
        this._sameSiteSelectElement = document.createElement("select");
203
        for (let sameSiteType of Object.values(WI.Cookie.SameSiteType)) {
204
            let optionElement = this._sameSiteSelectElement.appendChild(document.createElement("option"));
205
            optionElement.textContent = sameSiteType;
206
        }
207
        createRow("same-site", WI.unlocalizedString("SameSite"), this._sameSiteSelectElement);
208
209
        let toggleExpiresRow = () => {
210
            expiresInputRow.rowElement.hidden = this._sessionCheckboxElement.checked;
211
212
            this.update();
213
        };
214
215
        this._sessionCheckboxElement.addEventListener("change", (event) => {
216
            toggleExpiresRow();
217
        });
218
219
        toggleExpiresRow();
220
221
        this._serializedDataWhenShown = this.serializedData;
222
223
        this.content = popoverContentElement;
224
        this._presentOverTargetElement();
225
    }
226
227
    // Private
228
229
    _presentOverTargetElement()
230
    {
231
        if (!this._targetElement)
232
            return;
233
234
        let targetFrame = WI.Rect.rectFromClientRect(this._targetElement.getBoundingClientRect());
235
        this.present(targetFrame.pad(2), this._preferredEdges);
236
    }
237
238
    _defaultExpires()
239
    {
240
        return new Date(Date.now() + (1000 * 60 * 60 * 24)); // one day in the future
241
    }
242
243
    _parseExpires()
244
    {
245
        let timestamp = Date.parse(this._expiresInputElement.value || this._expiresInputElement.placeholder);
246
        if (timestamp < Date.now())
247
            return NaN;
248
        return timestamp;
249
    }
250
251
    _handleInputKeyDown(event)
252
    {
253
        if (event.key === "Enter" || event.key === "Esc")
254
            this.dismiss();
255
    }
256
};
- a/Source/WebInspectorUI/UserInterface/Views/CookieStorageContentView.js -3 / +75 lines
Lines 35-40 WI.CookieStorageContentView = class CookieStorageContentView extends WI.ContentV a/Source/WebInspectorUI/UserInterface/Views/CookieStorageContentView.js_sec1
35
        this._sortComparator = null;
35
        this._sortComparator = null;
36
        this._table = null;
36
        this._table = null;
37
37
38
        if (InspectorBackend.hasCommand("Page.setCookie")) {
39
            this._setCookieButtonNavigationItem = new WI.ButtonNavigationItem("cookie-storage-set-cookie", WI.UIString("Add Cookie"), "Images/Plus15.svg", 15, 15);
40
            this._setCookieButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleSetCookieButtonClick, this);
41
        }
42
38
        this._refreshButtonNavigationItem = new WI.ButtonNavigationItem("cookie-storage-refresh", WI.UIString("Refresh"), "Images/ReloadFull.svg", 13, 13);
43
        this._refreshButtonNavigationItem = new WI.ButtonNavigationItem("cookie-storage-refresh", WI.UIString("Refresh"), "Images/ReloadFull.svg", 13, 13);
39
        this._refreshButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._refreshButtonClicked, this);
44
        this._refreshButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._refreshButtonClicked, this);
40
    }
45
    }
Lines 43-49 WI.CookieStorageContentView = class CookieStorageContentView extends WI.ContentV a/Source/WebInspectorUI/UserInterface/Views/CookieStorageContentView.js_sec2
43
48
44
    get navigationItems()
49
    get navigationItems()
45
    {
50
    {
46
        return [this._refreshButtonNavigationItem];
51
        let navigationItems = [];
52
        if (this._setCookieButtonNavigationItem)
53
            navigationItems.push(this._setCookieButtonNavigationItem);
54
        navigationItems.push(this._refreshButtonNavigationItem);
55
        return navigationItems;
47
    }
56
    }
48
57
49
    saveToCookie(cookie)
58
    saveToCookie(cookie)
Lines 111-116 WI.CookieStorageContentView = class CookieStorageContentView extends WI.ContentV a/Source/WebInspectorUI/UserInterface/Views/CookieStorageContentView.js_sec3
111
        let contextMenu = WI.ContextMenu.createFromEvent(event);
120
        let contextMenu = WI.ContextMenu.createFromEvent(event);
112
121
113
        contextMenu.appendSeparator();
122
        contextMenu.appendSeparator();
123
124
        if (InspectorBackend.hasCommand("Page.setCookie")) {
125
            contextMenu.appendItem(WI.UIString("Edit"), () => {
126
                console.assert(!this._editingCookie);
127
                this._editingCookie = this._cookies[rowIndex];
128
129
                let popover = new WI.CookiePopover(this);
130
                popover.show(this._editingCookie, this._table.cellForRowAndColumn(rowIndex, this._table.columns[0]), [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_X]);
131
            });
132
        }
133
114
        contextMenu.appendItem(WI.UIString("Copy"), () => {
134
        contextMenu.appendItem(WI.UIString("Copy"), () => {
115
            let rowIndexes;
135
            let rowIndexes;
116
            if (table.isRowSelected(rowIndex))
136
            if (table.isRowSelected(rowIndex))
Lines 128-133 WI.CookieStorageContentView = class CookieStorageContentView extends WI.ContentV a/Source/WebInspectorUI/UserInterface/Views/CookieStorageContentView.js_sec4
128
            else
148
            else
129
                table.removeRow(rowIndex);
149
                table.removeRow(rowIndex);
130
        });
150
        });
151
131
        contextMenu.appendSeparator();
152
        contextMenu.appendSeparator();
132
    }
153
    }
133
154
Lines 157-162 WI.CookieStorageContentView = class CookieStorageContentView extends WI.ContentV a/Source/WebInspectorUI/UserInterface/Views/CookieStorageContentView.js_sec5
157
        return cell;
178
        return cell;
158
    }
179
    }
159
180
181
    // Popover delegate
182
183
    willDismissPopover(popover)
184
    {
185
        if (popover instanceof WI.CookiePopover) {
186
            this._willDismissCookiePopover(popover);
187
            return;
188
        }
189
190
        console.assert();
191
    }
192
160
    // Protected
193
    // Protected
161
194
162
    initialLayout()
195
    initialLayout()
Lines 315-320 WI.CookieStorageContentView = class CookieStorageContentView extends WI.ContentV a/Source/WebInspectorUI/UserInterface/Views/CookieStorageContentView.js_sec6
315
        this._sortComparator = (a, b) => reverseFactor * comparator(a, b);
348
        this._sortComparator = (a, b) => reverseFactor * comparator(a, b);
316
    }
349
    }
317
350
351
    async _willDismissCookiePopover(popover)
352
    {
353
        let editingCookie = this._editingCookie;
354
        this._editingCookie = null;
355
356
        let serializedData = popover.serializedData;
357
        if (!serializedData) {
358
            InspectorFrontendHost.beep();
359
            return;
360
        }
361
362
        let cookieToSet = WI.Cookie.fromPayload(serializedData);
363
364
        let cookieProtocolPayload = cookieToSet.toProtocol();
365
        if (!cookieProtocolPayload) {
366
            InspectorFrontendHost.beep();
367
            return;
368
        }
369
370
        let target = WI.assumingMainTarget();
371
372
        let promises = [];
373
        if (editingCookie)
374
            promises.push(target.PageAgent.deleteCookie(editingCookie.name, editingCookie.url));
375
        promises.push(target.PageAgent.setCookie(cookieProtocolPayload));
376
        promises.push(this._reloadCookies());
377
        await Promise.all(promises);
378
379
        let index = this._cookies.findIndex((existingCookie) => cookieToSet.equals(existingCookie));
380
        if (index >= 0)
381
            this._table.selectRow(index);
382
    }
383
384
    _handleSetCookieButtonClick(event)
385
    {
386
        let popover = new WI.CookiePopover(this);
387
        popover.show(null, this._setCookieButtonNavigationItem.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_X]);
388
    }
389
318
    _refreshButtonClicked(event)
390
    _refreshButtonClicked(event)
319
    {
391
    {
320
        this._reloadCookies();
392
        this._reloadCookies();
Lines 323-329 WI.CookieStorageContentView = class CookieStorageContentView extends WI.ContentV a/Source/WebInspectorUI/UserInterface/Views/CookieStorageContentView.js_sec7
323
    _reloadCookies()
395
    _reloadCookies()
324
    {
396
    {
325
        let target = WI.assumingMainTarget();
397
        let target = WI.assumingMainTarget();
326
        target.PageAgent.getCookies().then((payload) => {
398
        return target.PageAgent.getCookies().then((payload) => {
327
            this._cookies = this._filterCookies(payload.cookies.map(WI.Cookie.fromPayload));
399
            this._cookies = this._filterCookies(payload.cookies.map(WI.Cookie.fromPayload));
328
            this._updateSort();
400
            this._updateSort();
329
            this._table.reloadData();
401
            this._table.reloadData();
Lines 384-390 WI.CookieStorageContentView = class CookieStorageContentView extends WI.ContentV a/Source/WebInspectorUI/UserInterface/Views/CookieStorageContentView.js_sec8
384
        case "path":
456
        case "path":
385
            return cookie.path || missingValue;
457
            return cookie.path || missingValue;
386
        case "expires":
458
        case "expires":
387
            return cookie.expires ? new Date(cookie.expires).toLocaleString() : WI.UIString("Session");
459
            return (!cookie.session && cookie.expires) ? cookie.expires.toLocaleString() : WI.UIString("Session");
388
        case "size":
460
        case "size":
389
            return Number.bytesToString(cookie.size);
461
            return Number.bytesToString(cookie.size);
390
        case "secure":
462
        case "secure":
- a/Source/WebInspectorUI/UserInterface/Views/ResourceCookiesContentView.js -1 / +1 lines
Lines 98-104 WI.ResourceCookiesContentView = class ResourceCookiesContentView extends WI.Cont a/Source/WebInspectorUI/UserInterface/Views/ResourceCookiesContentView.js_sec1
98
            cell.textContent = cookie.path || emDash;
98
            cell.textContent = cookie.path || emDash;
99
            break;
99
            break;
100
        case "expires":
100
        case "expires":
101
            cell.textContent = cookie.expires ? cookie.expires.toLocaleString() : WI.UIString("Session");
101
            cell.textContent = (!cookie.session && cookie.expires) ? cookie.expires.toLocaleString() : WI.UIString("Session");
102
            break;
102
            break;
103
        case "maxAge":
103
        case "maxAge":
104
            cell.textContent = cookie.maxAge || emDash;
104
            cell.textContent = cookie.maxAge || emDash;
- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp +9 lines
Lines 654-659 void NetworkConnectionToWebProcess::getRawCookies(const URL& firstParty, const S a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp_sec1
654
    completionHandler(WTFMove(result));
654
    completionHandler(WTFMove(result));
655
}
655
}
656
656
657
void NetworkConnectionToWebProcess::setRawCookie(const WebCore::Cookie& cookie)
658
{
659
    auto* networkStorageSession = storageSession();
660
    if (!networkStorageSession)
661
        return;
662
663
    networkStorageSession->setCookie(cookie);
664
}
665
657
void NetworkConnectionToWebProcess::deleteCookie(const URL& url, const String& cookieName)
666
void NetworkConnectionToWebProcess::deleteCookie(const URL& url, const String& cookieName)
658
{
667
{
659
    auto* networkStorageSession = storageSession();
668
    auto* networkStorageSession = storageSession();
- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h +1 lines
Lines 209-214 private: a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h_sec1
209
    void setCookiesFromDOM(const URL& firstParty, const WebCore::SameSiteInfo&, const URL&, WebCore::FrameIdentifier, WebCore::PageIdentifier, WebCore::ShouldAskITP, const String&);
209
    void setCookiesFromDOM(const URL& firstParty, const WebCore::SameSiteInfo&, const URL&, WebCore::FrameIdentifier, WebCore::PageIdentifier, WebCore::ShouldAskITP, const String&);
210
    void cookieRequestHeaderFieldValue(const URL& firstParty, const WebCore::SameSiteInfo&, const URL&, Optional<WebCore::FrameIdentifier>, Optional<WebCore::PageIdentifier>, WebCore::IncludeSecureCookies, WebCore::ShouldAskITP, CompletionHandler<void(String cookieString, bool secureCookiesAccessed)>&&);
210
    void cookieRequestHeaderFieldValue(const URL& firstParty, const WebCore::SameSiteInfo&, const URL&, Optional<WebCore::FrameIdentifier>, Optional<WebCore::PageIdentifier>, WebCore::IncludeSecureCookies, WebCore::ShouldAskITP, CompletionHandler<void(String cookieString, bool secureCookiesAccessed)>&&);
211
    void getRawCookies(const URL& firstParty, const WebCore::SameSiteInfo&, const URL&, Optional<WebCore::FrameIdentifier>, Optional<WebCore::PageIdentifier>, WebCore::ShouldAskITP, CompletionHandler<void(Vector<WebCore::Cookie>&&)>&&);
211
    void getRawCookies(const URL& firstParty, const WebCore::SameSiteInfo&, const URL&, Optional<WebCore::FrameIdentifier>, Optional<WebCore::PageIdentifier>, WebCore::ShouldAskITP, CompletionHandler<void(Vector<WebCore::Cookie>&&)>&&);
212
    void setRawCookie(const WebCore::Cookie&);
212
    void deleteCookie(const URL&, const String& cookieName);
213
    void deleteCookie(const URL&, const String& cookieName);
213
214
214
    void registerFileBlobURL(const URL&, const String& path, SandboxExtension::Handle&&, const String& contentType);
215
    void registerFileBlobURL(const URL&, const String& path, SandboxExtension::Handle&&, const String& contentType);
- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in +1 lines
Lines 39-44 messages -> NetworkConnectionToWebProcess LegacyReceiver { a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in_sec1
39
    SetCookiesFromDOM(URL firstParty, struct WebCore::SameSiteInfo sameSiteInfo, URL url, WebCore::FrameIdentifier frameID, WebCore::PageIdentifier pageID, enum:bool WebCore::ShouldAskITP shouldAskITP, String cookieString)
39
    SetCookiesFromDOM(URL firstParty, struct WebCore::SameSiteInfo sameSiteInfo, URL url, WebCore::FrameIdentifier frameID, WebCore::PageIdentifier pageID, enum:bool WebCore::ShouldAskITP shouldAskITP, String cookieString)
40
    CookieRequestHeaderFieldValue(URL firstParty, struct WebCore::SameSiteInfo sameSiteInfo, URL url, Optional<WebCore::FrameIdentifier> frameID, Optional<WebCore::PageIdentifier> pageID, enum:bool WebCore::IncludeSecureCookies includeSecureCookies, enum:bool WebCore::ShouldAskITP shouldAskITP) -> (String cookieString, bool didAccessSecureCookies) Synchronous
40
    CookieRequestHeaderFieldValue(URL firstParty, struct WebCore::SameSiteInfo sameSiteInfo, URL url, Optional<WebCore::FrameIdentifier> frameID, Optional<WebCore::PageIdentifier> pageID, enum:bool WebCore::IncludeSecureCookies includeSecureCookies, enum:bool WebCore::ShouldAskITP shouldAskITP) -> (String cookieString, bool didAccessSecureCookies) Synchronous
41
    GetRawCookies(URL firstParty, struct WebCore::SameSiteInfo sameSiteInfo, URL url, Optional<WebCore::FrameIdentifier> frameID, Optional<WebCore::PageIdentifier> pageID, enum:bool WebCore::ShouldAskITP shouldAskITP) -> (Vector<WebCore::Cookie> cookies) Synchronous
41
    GetRawCookies(URL firstParty, struct WebCore::SameSiteInfo sameSiteInfo, URL url, Optional<WebCore::FrameIdentifier> frameID, Optional<WebCore::PageIdentifier> pageID, enum:bool WebCore::ShouldAskITP shouldAskITP) -> (Vector<WebCore::Cookie> cookies) Synchronous
42
    SetRawCookie(struct WebCore::Cookie cookie)
42
    DeleteCookie(URL url, String cookieName)
43
    DeleteCookie(URL url, String cookieName)
43
    DomCookiesForHost(String host, bool subscribeToCookieChangeNotifications) -> (Vector<WebCore::Cookie> cookies) Synchronous
44
    DomCookiesForHost(String host, bool subscribeToCookieChangeNotifications) -> (Vector<WebCore::Cookie> cookies) Synchronous
44
#if HAVE(COOKIE_CHANGE_LISTENER_API)
45
#if HAVE(COOKIE_CHANGE_LISTENER_API)
- a/Source/WebKit/WebProcess/WebPage/WebCookieJar.cpp +5 lines
Lines 228-233 bool WebCookieJar::getRawCookies(const WebCore::Document& document, const URL& u a/Source/WebKit/WebProcess/WebPage/WebCookieJar.cpp_sec1
228
    return true;
228
    return true;
229
}
229
}
230
230
231
void WebCookieJar::setRawCookie(const WebCore::Document& document, const Cookie& cookie)
232
{
233
    WebProcess::singleton().ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::SetRawCookie(cookie), 0);
234
}
235
231
void WebCookieJar::deleteCookie(const WebCore::Document& document, const URL& url, const String& cookieName)
236
void WebCookieJar::deleteCookie(const WebCore::Document& document, const URL& url, const String& cookieName)
232
{
237
{
233
    WebProcess::singleton().ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::DeleteCookie(url, cookieName), 0);
238
    WebProcess::singleton().ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::DeleteCookie(url, cookieName), 0);
- a/Source/WebKit/WebProcess/WebPage/WebCookieJar.h +1 lines
Lines 45-50 public: a/Source/WebKit/WebProcess/WebPage/WebCookieJar.h_sec1
45
    bool cookiesEnabled(const WebCore::Document&) const final;
45
    bool cookiesEnabled(const WebCore::Document&) const final;
46
    std::pair<String, WebCore::SecureCookiesAccessed> cookieRequestHeaderFieldValue(const URL& firstParty, const WebCore::SameSiteInfo&, const URL&, Optional<WebCore::FrameIdentifier>, Optional<WebCore::PageIdentifier>, WebCore::IncludeSecureCookies) const final;
46
    std::pair<String, WebCore::SecureCookiesAccessed> cookieRequestHeaderFieldValue(const URL& firstParty, const WebCore::SameSiteInfo&, const URL&, Optional<WebCore::FrameIdentifier>, Optional<WebCore::PageIdentifier>, WebCore::IncludeSecureCookies) const final;
47
    bool getRawCookies(const WebCore::Document&, const URL&, Vector<WebCore::Cookie>&) const final;
47
    bool getRawCookies(const WebCore::Document&, const URL&, Vector<WebCore::Cookie>&) const final;
48
    void setRawCookie(const WebCore::Document&, const WebCore::Cookie&) final;
48
    void deleteCookie(const WebCore::Document&, const URL&, const String& cookieName) final;
49
    void deleteCookie(const WebCore::Document&, const URL&, const String& cookieName) final;
49
50
50
    void cookiesAdded(const String& host, const Vector<WebCore::Cookie>&);
51
    void cookiesAdded(const String& host, const Vector<WebCore::Cookie>&);
- a/LayoutTests/ChangeLog +15 lines
Lines 1-3 a/LayoutTests/ChangeLog_sec1
1
2020-03-20  Devin Rousso  <drousso@apple.com>
2
3
        Web Inspector: support editing cookie key/values from inspector
4
        https://bugs.webkit.org/show_bug.cgi?id=31157
5
        <rdar://problem/19281523>
6
7
        Reviewed by NOBODY (OOPS!).
8
9
        * http/tests/inspector/page/setCookie.html: Added.
10
        * http/tests/inspector/page/setCookie-expected.txt: Added.
11
12
        * inspector/unit-tests/number-utilities.html:
13
        * inspector/unit-tests/number-utilities-expected.txt:
14
        Drive-by: add tests for `Number.prototype.maxDecimals`.
15
1
2020-03-21  Simon Fraser  <simon.fraser@apple.com>
16
2020-03-21  Simon Fraser  <simon.fraser@apple.com>
2
17
3
        LayoutTest fast/scrolling/scroll-container-horizontally.html frequently times out & fails
18
        LayoutTest fast/scrolling/scroll-container-horizontally.html frequently times out & fails
- a/LayoutTests/http/tests/inspector/page/setCookie-expected.txt +121 lines
Line 0 a/LayoutTests/http/tests/inspector/page/setCookie-expected.txt_sec1
1
Test for the Page.setCookie.
2
3
4
== Running test suite: Page.setCookie
5
-- Running test case: Page.setCookie.Valid
6
PASS: Should have been able to set all cookies.
7
8
-- Running test case: Page.setCookie.Invalid
9
Setting cookie {} ...
10
PASS: Should produce an exception.
11
Error: Invalid value for key name in given cookie
12
13
Setting cookie {"name":null} ...
14
PASS: Should produce an exception.
15
Error: Invalid value for key name in given cookie
16
17
Setting cookie {"name":-1} ...
18
PASS: Should produce an exception.
19
Error: Invalid value for key name in given cookie
20
21
Setting cookie {"name":"name"} ...
22
PASS: Should produce an exception.
23
Error: Invalid value for key value in given cookie
24
25
Setting cookie {"name":"name","value":null} ...
26
PASS: Should produce an exception.
27
Error: Invalid value for key value in given cookie
28
29
Setting cookie {"name":"name","value":-1} ...
30
PASS: Should produce an exception.
31
Error: Invalid value for key value in given cookie
32
33
Setting cookie {"name":"name","value":"value"} ...
34
PASS: Should produce an exception.
35
Error: Invalid value for key domain in given cookie
36
37
Setting cookie {"name":"name","value":"value","domain":null} ...
38
PASS: Should produce an exception.
39
Error: Invalid value for key domain in given cookie
40
41
Setting cookie {"name":"name","value":"value","domain":-1} ...
42
PASS: Should produce an exception.
43
Error: Invalid value for key domain in given cookie
44
45
Setting cookie {"name":"name","value":"value","domain":"webkit.org"} ...
46
PASS: Should produce an exception.
47
Error: Invalid value for key path in given cookie
48
49
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":null} ...
50
PASS: Should produce an exception.
51
Error: Invalid value for key path in given cookie
52
53
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":-1} ...
54
PASS: Should produce an exception.
55
Error: Invalid value for key path in given cookie
56
57
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/"} ...
58
PASS: Should produce an exception.
59
Error: Invalid value for key httpOnly in given cookie
60
61
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":null} ...
62
PASS: Should produce an exception.
63
Error: Invalid value for key httpOnly in given cookie
64
65
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":"INVALID"} ...
66
PASS: Should produce an exception.
67
Error: Invalid value for key httpOnly in given cookie
68
69
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":10000000000} ...
70
PASS: Should produce an exception.
71
Error: Invalid value for key httpOnly in given cookie
72
73
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":10000000000,"session":null} ...
74
PASS: Should produce an exception.
75
Error: Invalid value for key httpOnly in given cookie
76
77
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":10000000000,"session":"INVALID"} ...
78
PASS: Should produce an exception.
79
Error: Invalid value for key httpOnly in given cookie
80
81
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":10000000000,"session":true} ...
82
PASS: Should produce an exception.
83
Error: Invalid value for key httpOnly in given cookie
84
85
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":10000000000,"session":true,"httpOnly":null} ...
86
PASS: Should produce an exception.
87
Error: Invalid value for key httpOnly in given cookie
88
89
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":10000000000,"session":true,"httpOnly":"INVALID"} ...
90
PASS: Should produce an exception.
91
Error: Invalid value for key httpOnly in given cookie
92
93
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":10000000000,"session":true,"httpOnly":true} ...
94
PASS: Should produce an exception.
95
Error: Invalid value for key secure in given cookie
96
97
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":10000000000,"session":true,"httpOnly":true,"secure":null} ...
98
PASS: Should produce an exception.
99
Error: Invalid value for key secure in given cookie
100
101
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":10000000000,"session":true,"httpOnly":true,"secure":"INVALID"} ...
102
PASS: Should produce an exception.
103
Error: Invalid value for key secure in given cookie
104
105
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":10000000000,"session":true,"httpOnly":true,"secure":true} ...
106
PASS: Should produce an exception.
107
Error: Invalid value for key sameSite in given cookie
108
109
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":10000000000,"session":true,"httpOnly":true,"secure":true,"sameSite":null} ...
110
PASS: Should produce an exception.
111
Error: Invalid value for key sameSite in given cookie
112
113
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":10000000000,"session":true,"httpOnly":true,"secure":true,"sameSite":-1} ...
114
PASS: Should produce an exception.
115
Error: Invalid value for key sameSite in given cookie
116
117
Setting cookie {"name":"name","value":"value","domain":"webkit.org","path":"/","expires":10000000000,"session":true,"httpOnly":true,"secure":true,"sameSite":"INVALID"} ...
118
PASS: Should produce an exception.
119
Error: Invalid value for key sameSite in given cookie
120
121
- a/LayoutTests/http/tests/inspector/page/setCookie.html +122 lines
Line 0 a/LayoutTests/http/tests/inspector/page/setCookie.html_sec1
1
<!DOCTYPE html>
2
<html>
3
<head>
4
<script src="../resources/inspector-test.js"></script>
5
<script>
6
if (window.testRunner)
7
    testRunner.setAlwaysAcceptCookies(true);
8
9
function test()
10
{
11
    const millisecondsPerDay = 1000 * 60 * 60 * 24;
12
    let tomorrow = new Date(Date.now().maxDecimals(-3) + millisecondsPerDay);
13
14
    let suite = InspectorTest.createAsyncSuite("Page.setCookie");
15
16
    suite.addTestCase({
17
        name: "Page.setCookie.Valid",
18
        description: "Check that setting a valid cookies works.",
19
        async test() {
20
            let cookiesToSet = [
21
                {
22
                    expires: tomorrow,
23
                    session: false,
24
                    path: "/",
25
                    domain: "127.0.0.1",
26
                    secure: false,
27
                    httpOnly: false,
28
                    sameSite: WI.Cookie.SameSiteType.Lax,
29
                },
30
                {
31
                    expires: tomorrow,
32
                    session: true,
33
                    path: "/inspector/",
34
                    domain: "127.0.0.1",
35
                    secure: true,
36
                    httpOnly: true,
37
                    sameSite: WI.Cookie.SameSiteType.Strict,
38
                },
39
            ].map((options, i) => new WI.Cookie(WI.Cookie.Type.Response, "TestCookieName" + i, "TestCookieValue" + i, options));
40
41
            for (let cookieToSet of cookiesToSet)
42
                await PageAgent.setCookie(cookieToSet.toProtocol());
43
44
            let getCookiesResult = await PageAgent.getCookies();
45
            let setCookies = getCookiesResult.cookies.map(WI.Cookie.fromPayload);
46
47
            let found = true;
48
            for (let cookieToSet of cookiesToSet) {
49
                if (!setCookies.find((setCookie) => cookieToSet.equals(setCookie))) {
50
                    found = false;
51
                    InspectorTest.fail("Missing cookie:");
52
                    InspectorTest.json(cookieToSet);
53
                }
54
            }
55
            InspectorTest.expectTrue(found, "Should have been able to set all cookies.");
56
        },
57
    });
58
59
    suite.addTestCase({
60
        name: "Page.setCookie.Invalid",
61
        description: "Check that the PageAgent correctly parses the given cookie JSON object.",
62
        async test() {
63
            const invalidValue = null;
64
            const invalidString = -1;
65
            const invalidNumber = "INVALID";
66
            const invalidBoolean = invalidNumber;
67
68
            const cookies = [
69
                {},
70
71
                {name: invalidValue},
72
                {name: invalidString},
73
                {name: "name"},
74
75
                {name: "name", value: invalidValue},
76
                {name: "name", value: invalidString},
77
                {name: "name", value: "value"},
78
79
                {name: "name", value: "value", domain: invalidValue},
80
                {name: "name", value: "value", domain: invalidString},
81
                {name: "name", value: "value", domain: "webkit.org"},
82
83
                {name: "name", value: "value", domain: "webkit.org", path: invalidValue},
84
                {name: "name", value: "value", domain: "webkit.org", path: invalidString},
85
                {name: "name", value: "value", domain: "webkit.org", path: "/"},
86
87
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: invalidValue},
88
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: invalidNumber},
89
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: 10000000000},
90
91
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: 10000000000, session: invalidValue},
92
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: 10000000000, session: invalidBoolean},
93
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: 10000000000, session: true},
94
95
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: 10000000000, session: true, httpOnly: invalidValue},
96
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: 10000000000, session: true, httpOnly: invalidBoolean},
97
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: 10000000000, session: true, httpOnly: true},
98
99
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: 10000000000, session: true, httpOnly: true, secure: invalidValue},
100
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: 10000000000, session: true, httpOnly: true, secure: invalidBoolean},
101
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: 10000000000, session: true, httpOnly: true, secure: true},
102
103
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: 10000000000, session: true, httpOnly: true, secure: true, sameSite: invalidValue},
104
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: 10000000000, session: true, httpOnly: true, secure: true, sameSite: invalidString},
105
                {name: "name", value: "value", domain: "webkit.org", path: "/", expires: 10000000000, session: true, httpOnly: true, secure: true, sameSite: "INVALID"},
106
            ];
107
            for (let cookie of cookies) {
108
                InspectorTest.log("Setting cookie " + JSON.stringify(cookie) + " ...");
109
                await InspectorTest.expectException(() => PageAgent.setCookie(cookie));
110
                InspectorTest.newline();
111
            }
112
        },
113
    });
114
115
    suite.runTestCasesAndFinish();
116
}
117
</script>
118
</head>
119
<body onload="runTestHTTPS()">
120
    <p>Test for the Page.setCookie.</p>
121
</body>
122
</html>
- a/LayoutTests/inspector/unit-tests/number-utilities-expected.txt +11 lines
Lines 87-89 PASS: -100000000 should have 9 digits a/LayoutTests/inspector/unit-tests/number-utilities-expected.txt_sec1
87
PASS: 1000000000 should have 10 digits
87
PASS: 1000000000 should have 10 digits
88
PASS: -1000000000 should have 10 digits
88
PASS: -1000000000 should have 10 digits
89
89
90
-- Running test case: Number.prototype.maxDecimals
91
PASS: maxDecimals with a negative argument more than the number of digits should be 0
92
PASS: maxDecimals with a negative argument more equal to the number of digits should be 0
93
PASS: maxDecimals with -2 should truncate that 2 units in front of the decimal
94
PASS: maxDecimals with -1 should truncate that 1 units in front of the decimal
95
PASS: maxDecimals with 0 should round the value
96
PASS: maxDecimals with 1 should round after the 1st decimal
97
PASS: maxDecimals with 2 should round after the 2nd decimal
98
PASS: maxDecimals with a positive argument equal to the number of digits should not modify the number
99
PASS: maxDecimals with a positive argument greater than the number of digits should not modify the number
100
- a/LayoutTests/inspector/unit-tests/number-utilities.html +17 lines
Lines 134-139 function test() a/LayoutTests/inspector/unit-tests/number-utilities.html_sec1
134
        }
134
        }
135
    });
135
    });
136
136
137
    suite.addTestCase({
138
        name: "Number.prototype.maxDecimals",
139
        test() {
140
            const n = 123.456;
141
142
            InspectorTest.expectEqual(n.maxDecimals(-4), 0, "maxDecimals with a negative argument more than the number of digits should be 0");
143
            InspectorTest.expectEqual(n.maxDecimals(-3), 0, "maxDecimals with a negative argument more equal to the number of digits should be 0");
144
            InspectorTest.expectEqual(n.maxDecimals(-2), 100, "maxDecimals with -2 should truncate that 2 units in front of the decimal");
145
            InspectorTest.expectEqual(n.maxDecimals(-1), 120, "maxDecimals with -1 should truncate that 1 units in front of the decimal");
146
            InspectorTest.expectEqual(n.maxDecimals(0), 123, "maxDecimals with 0 should round the value");
147
            InspectorTest.expectEqual(n.maxDecimals(1), 123.5, "maxDecimals with 1 should round after the 1st decimal");
148
            InspectorTest.expectEqual(n.maxDecimals(2), 123.46, "maxDecimals with 2 should round after the 2nd decimal");
149
            InspectorTest.expectEqual(n.maxDecimals(3), 123.456, "maxDecimals with a positive argument equal to the number of digits should not modify the number");
150
            InspectorTest.expectEqual(n.maxDecimals(4), 123.456, "maxDecimals with a positive argument greater than the number of digits should not modify the number");
151
        },
152
    });
153
137
    suite.runTestCasesAndFinish();
154
    suite.runTestCasesAndFinish();
138
}
155
}
139
</script>
156
</script>

Return to Bug 31157