diff --git a/platform/platform-api/src/com/intellij/ui/jcef/JBCefCookie.java b/platform/platform-api/src/com/intellij/ui/jcef/JBCefCookie.java new file mode 100644 index 000000000000..0a36c59ebba1 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/jcef/JBCefCookie.java @@ -0,0 +1,114 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.ui.jcef; + +import org.cef.network.CefCookie; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Date; +import java.util.Objects; + +/** + * A wrapper over {@link CefCookie}. + * + * @author Aleksey.Rostovskiy + */ +public class JBCefCookie { + private final CefCookie myCefCookie; + + public JBCefCookie(CefCookie cefCookie) { + myCefCookie = cefCookie; + } + + public JBCefCookie(@NotNull String name, + @NotNull String value, + @NotNull String domain, + @NotNull String path, + boolean secure, + boolean httponly, + @Nullable Date creation, + @Nullable Date lastAccess, + boolean hasExpires, + @Nullable Date expires) { + this(new CefCookie(name, value, domain, path, secure, httponly, creation, lastAccess, hasExpires, expires)); + } + + public JBCefCookie(@NotNull String name, + @NotNull String value, + @NotNull String domain, + @NotNull String path, + boolean secure, + boolean httponly) { + this(name, value, domain, path, secure, httponly, null, null, false, null); + } + + @NotNull + public CefCookie getCefCookie() { + return myCefCookie; + } + + @NotNull + public String getName() { + return myCefCookie.name; + } + + @NotNull + public String getValue() { + return myCefCookie.value; + } + + @NotNull + public String getDomain() { + return myCefCookie.domain; + } + + @NotNull + public String getPath() { + return myCefCookie.path; + } + + public boolean isSecure() { + return myCefCookie.secure; + } + + public boolean isHttpOnly() { + return myCefCookie.httponly; + } + + @Nullable + public Date getCreation() { + return myCefCookie.creation; + } + + @Nullable + public Date getLastAccess() { + return myCefCookie.lastAccess; + } + + public boolean hasExpires() { + return myCefCookie.hasExpires; + } + + @Nullable + public Date getExpires() { + return myCefCookie.expires; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + JBCefCookie cookie = (JBCefCookie)o; + return getName().equals(cookie.getName()) && + getValue().equals(cookie.getValue()) && + getDomain().equals(cookie.getDomain()) && + getPath().equals(cookie.getPath()) && + isSecure() == cookie.isSecure() && + isHttpOnly() == cookie.isHttpOnly(); + } + + @Override + public int hashCode() { + return Objects.hash(getName(), getValue(), getDomain(), getPath(), isSecure(), isHttpOnly()); + } +} diff --git a/platform/platform-api/src/com/intellij/ui/jcef/JBCefCookieManager.java b/platform/platform-api/src/com/intellij/ui/jcef/JBCefCookieManager.java index 0c9cd8790984..997f96831cf0 100644 --- a/platform/platform-api/src/com/intellij/ui/jcef/JBCefCookieManager.java +++ b/platform/platform-api/src/com/intellij/ui/jcef/JBCefCookieManager.java @@ -11,6 +11,8 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; @@ -24,7 +26,7 @@ import static com.intellij.openapi.util.Clock.getTime; *

* Use {@link #getCookies()} and others for retrieving cookies synchronously. *

- * Use {@link #setCookie(String, CefCookie, boolean)} and others for setting cookie. Can be used both with synchronization or without one. + * Use {@link #setCookie(String, JBCefCookie, boolean)} and others for setting cookie. Can be used both with synchronization or without one. *

* Use {@link #deleteCookies(boolean)} and others for deleting cookies. Can be used both with synchronization or without one. * @@ -57,16 +59,14 @@ public class JBCefCookieManager { /** * @see JBCefCookieManager#getCookies(String, Boolean, Integer) */ - public @Nullable List getCookies() { + public @Nullable List getCookies() { return getCookies(null, false, null); } /** - * TODO[hatari] Doesn't work now, waiting for JCEF library update

- * * @see JBCefCookieManager#getCookies(String, Boolean, Integer) */ - public @Nullable List getCookies(@NotNull String url) { + public @Nullable List getCookies(@NotNull String url) { return getCookies(url, false, null); } @@ -78,9 +78,9 @@ public class JBCefCookieManager { * @param includeHttpOnly include only true HTTP-only cookies. * @param maxTimeToWait time to wait getting cookies in ms, default value is * {@link JBCefCookieManager#DEFAULT_TIMEOUT_FOR_SYNCHRONOUS_FUNCTION}. - * @return list of {@link CefCookie} or null if cookies cannot be accessed + * @return list of {@link JBCefCookie} or null if cookies cannot be accessed */ - public @Nullable List getCookies(@Nullable String url, @Nullable Boolean includeHttpOnly, @Nullable Integer maxTimeToWait) { + public @Nullable List getCookies(@Nullable String url, @Nullable Boolean includeHttpOnly, @Nullable Integer maxTimeToWait) { long startTime = getTime(); boolean httpOnly = includeHttpOnly != null ? includeHttpOnly : false; @@ -91,7 +91,6 @@ public class JBCefCookieManager { boolean result; if (url != null) { - // TODO[hatari] Doesn't work now, waiting for JCEF library update

result = myCefCookieManager.visitUrlCookies(url, httpOnly, cookieVisitor); } else { @@ -123,20 +122,18 @@ public class JBCefCookieManager { } /** - * @see JBCefCookieManager#setCookie(String, CefCookie, Integer) + * @see JBCefCookieManager#setCookie(String, JBCefCookie, Integer) */ - public boolean setCookie(@NotNull String url, @NotNull CefCookie cefCookie, boolean doSync) { + public boolean setCookie(@NotNull String url, @NotNull JBCefCookie jbCefCookie, boolean doSync) { if (doSync) { - return setCookie(url, cefCookie, null); + return setCookie(url, jbCefCookie, null); } else { - return myCefCookieManager.setCookie(url, cefCookie); + return myCefCookieManager.setCookie(url, jbCefCookie.getCefCookie()); } } /** - * TODO[hatari] Doesn't work now, waiting for JCEF library update

- *

* Sets a cookie given a valid URL and explicit user-provided cookie attributes. * Underlying native method {@link CefCookieManager#setCookie(String, CefCookie)} is asynchronous. * This method is synchronous and will wait up to `maxTimeToWait` ms. @@ -145,15 +142,35 @@ public class JBCefCookieManager { * {@link JBCefCookieManager#DEFAULT_TIMEOUT_FOR_SYNCHRONOUS_FUNCTION} ms. * @return true if setting the cookie was successful. */ - public boolean setCookie(@NotNull String url, @NotNull CefCookie cefCookie, @Nullable Integer maxTimeToWait) { + public boolean setCookie(@NotNull String url, @NotNull JBCefCookie jbCefCookie, @Nullable Integer maxTimeToWait) { + try { + URI uri = new URI(url); + String scheme = uri.getScheme(); + String domain = uri.getHost(); + domain = domain.startsWith("www") ? domain : "." + domain; + + if (scheme.equals("http") && jbCefCookie.isSecure()) { + LOG.warn("Cannot set cookie without secure flag for HTTPS web-site"); + return false; + } + if (!domain.contains(jbCefCookie.getDomain())) { + LOG.warn("Cookie domain `" + jbCefCookie.getDomain() + "` doesn't match URL host `" + domain + "`"); + return false; + } + } + catch (URISyntaxException e) { + LOG.error(e); + return false; + } + int timeout = maxTimeToWait != null ? maxTimeToWait : DEFAULT_TIMEOUT_FOR_SYNCHRONOUS_FUNCTION; IntFunction checkFunction = (timeoutForCheck) -> { - List cookies = getCookies(url, null, timeoutForCheck); + List cookies = getCookies(url, null, timeoutForCheck); if (cookies == null) { return false; } - return cookies.contains(cefCookie); + return cookies.contains(jbCefCookie); }; myLock.lock(); @@ -163,7 +180,7 @@ public class JBCefCookieManager { LOG.debug("Cookie is already set"); return true; } - if (!myCefCookieManager.setCookie(url, cefCookie)) { + if (!myCefCookieManager.setCookie(url, jbCefCookie.getCefCookie())) { LOG.error("Posting task to set cookie is failed"); return false; } @@ -204,7 +221,7 @@ public class JBCefCookieManager { public boolean deleteCookies(boolean doSync) { if (doSync) { return deleteCookies("", "", (timeout) -> { - List cookies = getCookies(null, false, timeout); + List cookies = getCookies(null, false, timeout); if (cookies == null) { return false; } @@ -225,7 +242,7 @@ public class JBCefCookieManager { public boolean deleteCookies(@NotNull String url, boolean doSync) { if (doSync) { return deleteCookies(url, "", (timeout) -> { - List cookies = getCookies(url, false, timeout); + List cookies = getCookies(url, false, timeout); if (cookies == null) { return false; } @@ -259,11 +276,11 @@ public class JBCefCookieManager { */ public boolean deleteCookies(@NotNull String url, @NotNull String cookieName, @Nullable Integer maxTimeToWait) { IntFunction checkFunction = (timeout) -> { - List cookies = getCookies(url, false, timeout); + List cookies = getCookies(url, false, timeout); if (cookies == null) { return false; } - return cookies.stream().allMatch(cefCookie -> cefCookie.name != cookieName); + return cookies.stream().noneMatch(cefCookie -> cefCookie.getName().equals(cookieName)); }; return deleteCookies(url, cookieName, checkFunction, maxTimeToWait); @@ -327,7 +344,7 @@ public class JBCefCookieManager { } private static class JBCookieVisitor implements CefCookieVisitor { - final List myCefCookies = new ArrayList<>(); + final List myCefCookies = new ArrayList<>(); final CountDownLatch myCountDownLatch; JBCookieVisitor(CountDownLatch countDownLatch) { @@ -338,7 +355,7 @@ public class JBCefCookieManager { // TODO[hatari]: This method may never be called if no cookies are found. // So CountDownLatch can't countDown() well except timeout. public boolean visit(CefCookie cookie, int count, int total, BoolRef delete) { - myCefCookies.add(cookie); + myCefCookies.add(new JBCefCookie(cookie)); if (count >= total - 1) { // last element myCountDownLatch.countDown(); @@ -346,7 +363,7 @@ public class JBCefCookieManager { return true; } - public List getCookies() { + public List getCookies() { return myCefCookies; } } diff --git a/platform/platform-impl/src/com/intellij/internal/jcef/CookieManagerDialog.java b/platform/platform-impl/src/com/intellij/internal/jcef/CookieManagerDialog.java index acd90c114796..bbd25948b6ff 100644 --- a/platform/platform-impl/src/com/intellij/internal/jcef/CookieManagerDialog.java +++ b/platform/platform-impl/src/com/intellij/internal/jcef/CookieManagerDialog.java @@ -4,10 +4,10 @@ package com.intellij.internal.jcef; import com.intellij.ui.jcef.JBCefBrowser; +import com.intellij.ui.jcef.JBCefCookie; import com.intellij.ui.jcef.JBCefCookieManager; import com.intellij.ui.table.JBTable; import com.intellij.util.ui.JBUI; -import org.cef.network.CefCookie; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -22,7 +22,6 @@ import java.util.List; class CookieManagerDialog extends JDialog { private static final String myTitle = "Cookie Manager"; private static final String myDeleteCookiesButtonText = "Delete All Cookies"; - private static final String myAdditionalButtonText = "Test SetCookie"; @SuppressWarnings("unused") private final JBCefBrowser myJBCefBrowser; private final JBCefCookieManager myJBCefCookieManager; private final CookieTableModel myTableModel = new CookieTableModel(); @@ -46,7 +45,7 @@ class CookieManagerDialog extends JDialog { @Override public void actionPerformed(ActionEvent e) { if (myJBCefCookieManager.deleteCookies(true)) { - List cookies = myJBCefCookieManager.getCookies(); + List cookies = myJBCefCookieManager.getCookies(); if (cookies != null) { update(cookies); } @@ -55,41 +54,10 @@ class CookieManagerDialog extends JDialog { }); controlPanel.add(myDeleteCookiesButton); - final JButton myAdditionalButton = new JButton(myAdditionalButtonText); - myAdditionalButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - List cookies = myJBCefCookieManager.getCookies(); - if (cookies == null || cookies.isEmpty()) return; - - CefCookie firstCookie = cookies.get(0); - CefCookie newCookie = new CefCookie( - "Cookie", - "Value", - firstCookie.domain, - firstCookie.path, - firstCookie.secure, - firstCookie.secure, - firstCookie.creation, - firstCookie.lastAccess, - firstCookie.hasExpires, - firstCookie.expires - ); - - if (myJBCefCookieManager.setCookie("http://maps.google.com", newCookie, true)) { - cookies = myJBCefCookieManager.getCookies(); - if (cookies != null) { - update(cookies); - } - } - } - }); - controlPanel.add(myAdditionalButton); - add(controlPanel, BorderLayout.SOUTH); } - public void update(List cefCookies) { + public void update(List cefCookies) { myTableModel.clear(); myTableModel.show(cefCookies); } @@ -99,18 +67,18 @@ class CookieManagerDialog extends JDialog { new String[]{"Name", "Value", "Domain", "Path", "Secure", "HTTP only", "Created", "Last Access", "Expires"}; private final ArrayList rowData = new ArrayList<>(); - private void show(@NotNull List cefCookies) { - for (CefCookie cookie : cefCookies) { + private void show(@NotNull List cefCookies) { + for (JBCefCookie cookie : cefCookies) { Object[] entry = { - cookie.name, - cookie.value, - cookie.domain, - cookie.path, - Boolean.valueOf(cookie.secure), - Boolean.valueOf(cookie.httponly), - cookie.creation, - cookie.lastAccess, - cookie.expires + cookie.getName(), + cookie.getValue(), + cookie.getDomain(), + cookie.getPath(), + cookie.isSecure(), + cookie.isHttpOnly(), + cookie.getCreation(), + cookie.getLastAccess(), + cookie.getExpires() }; int row = rowData.size(); rowData.add(entry); diff --git a/platform/platform-impl/src/com/intellij/internal/jcef/WebBrowser.java b/platform/platform-impl/src/com/intellij/internal/jcef/WebBrowser.java index 4fbe1154b4aa..15092629d1e0 100644 --- a/platform/platform-impl/src/com/intellij/internal/jcef/WebBrowser.java +++ b/platform/platform-impl/src/com/intellij/internal/jcef/WebBrowser.java @@ -9,10 +9,10 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.wm.impl.IdeFrameImpl; import com.intellij.ui.jcef.JBCefApp; import com.intellij.ui.jcef.JBCefBrowser; +import com.intellij.ui.jcef.JBCefCookie; import org.cef.browser.CefBrowser; import org.cef.browser.CefFrame; import org.cef.handler.CefLoadHandlerAdapter; -import org.cef.network.CefCookie; import org.cef.network.CefRequest; import org.jetbrains.annotations.NotNull; @@ -84,7 +84,7 @@ public class WebBrowser extends AnAction implements DumbAware { @Override public void actionPerformed(ActionEvent e) { myCookieManagerDialog.setVisible(true); - List cookies = myJBCefBrowser.getJBCefCookieManager().getCookies(); + List cookies = myJBCefBrowser.getJBCefCookieManager().getCookies(); if (cookies != null) { myCookieManagerDialog.update(cookies); }