Fix clipboard handling in safari (#6917)

* fix clipboard handling in safari

closes #6911, #6909

Co-authored-by: Can Yesilyurt <36952967+canyesilyurt@users.noreply.github.com>

* [autofix.ci] apply automated fixes

* update dependencies

---------

Co-authored-by: Can Yesilyurt <36952967+canyesilyurt@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Maximilian Hils 2024-06-12 14:59:40 +02:00 committed by GitHub
parent 40ae9ff05b
commit 832e735b0a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 2229 additions and 2815 deletions

View File

@ -21,6 +21,8 @@
([#6876](https://github.com/mitmproxy/mitmproxy/pull/6876), @errorxyz)
* Add an API to parse HTTPS records from DNS RDATA.
([#6884](https://github.com/mitmproxy/mitmproxy/pull/6884), @errorxyz)
* Fix flow export in mitmweb for Safari
([#6917](https://github.com/mitmproxy/mitmproxy/pull/6917), @mhils, @canyesilyurt)
* Releases now come with a Sigstore attestations file to demonstrate build provenance.
([f05c050](https://github.com/mitmproxy/mitmproxy/commit/f05c050f615b9ab9963707944c893bc94e738525), @mhils)

4911
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
"prettier": "prettier --write ."
},
"dependencies": {
"@popperjs/core": "^2.9.3",
"@popperjs/core": "^2.11.8",
"bootstrap": "^3.4.1",
"classnames": "^2.3.1",
"codemirror": "^5.62.3",
@ -29,7 +29,7 @@
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@testing-library/user-event": "^13.2.1",
"@types/jest": "^27.0.1",
"@types/jest": "^29.5.12",
"@types/redux-mock-store": "^1.0.3",
"esbuild": "^0.12.21",
"esbuild-jest": "^0.5.0",
@ -43,7 +43,8 @@
"gulp-plumber": "^1.2.1",
"gulp-replace": "^1.1.3",
"gulp-sourcemaps": "^3.0.0",
"jest": "^27.0.6",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-fetch-mock": "^3.0.3",
"prettier": "2.8.4",
"react-test-renderer": "^17.0.2",

View File

@ -1,8 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`EventList Component should render correctly 1`] = `
Object {
"vScroll": Object {
{
"vScroll": {
"end": 1,
"paddingBottom": 18,
"paddingTop": 0,
@ -12,14 +12,14 @@ Object {
`;
exports[`EventList Component should render correctly 2`] = `
Object {
"events": Array [
Object {
{
"events": [
{
"id": 1,
"level": "info",
"message": "foo",
},
Object {
{
"id": 2,
"level": "error",
"message": "bar",

View File

@ -10,7 +10,7 @@ exports[`FilterInput Component should render correctly 1`] = `
<i
className="fa fa-fw fa-foo"
style={
Object {
{
"color": "red",
}
}

View File

@ -4,7 +4,7 @@ exports[`EventLog Component should connect to state and render correctly 1`] = `
<div
className="eventlog"
style={
Object {
{
"height": 200,
}
}

View File

@ -8,7 +8,7 @@ exports[`FlowTable Component should render correctly 1`] = `
<table>
<thead
style={
Object {
{
"transform": "translateY(0px)",
}
}
@ -67,7 +67,7 @@ exports[`FlowTable Component should render correctly 1`] = `
<tbody>
<tr
style={
Object {
{
"height": 0,
}
}
@ -107,7 +107,7 @@ exports[`FlowTable Component should render correctly 1`] = `
<td
className="col-status"
style={
Object {
{
"color": "darkgreen",
}
}
@ -131,7 +131,7 @@ exports[`FlowTable Component should render correctly 1`] = `
</tr>
<tr
style={
Object {
{
"height": 0,
}
}

View File

@ -1,27 +0,0 @@
// Adapted from https://stackoverflow.com/a/65996386/934719
/**
* `navigator.clipboard.writeText()`, but with an additional fallback for non-secure contexts.
*/
export function copyToClipboard(text: string): Promise<void> {
// navigator clipboard requires a security context such as https
if (navigator.clipboard && window.isSecureContext) {
return navigator.clipboard.writeText(text);
} else {
let t = document.createElement("textarea");
t.value = text;
t.style.position = "absolute";
t.style.opacity = "0";
document.body.appendChild(t);
try {
t.focus();
t.select();
document.execCommand("copy");
return Promise.resolve();
} catch (err) {
alert(text);
return Promise.reject(err);
} finally {
t.remove();
}
}
}

View File

@ -1,14 +1,22 @@
import { runCommand } from "../utils";
import { copyToClipboard, runCommand } from "../utils";
import { Flow } from "../flow";
import { copyToClipboard } from "../contrib/clipboard";
export const copy = async (flow: Flow, format: string): Promise<void> => {
let ret = await runCommand("export", format, `@${flow.id}`);
if (ret.value) {
await copyToClipboard(ret.value);
} else if (ret.error) {
alert(ret.error);
} else {
console.error(ret);
// Safari: We need to call copyToClipboard _right away_ with a promise,
// otherwise we're loosing user intent and can't copy anymore.
let formatted = (async () => {
let ret = await runCommand("export", format, `@${flow.id}`);
if (ret.value) {
return ret.value;
} else if (ret.error) {
throw ret.error;
} else {
throw ret;
}
})();
try {
await copyToClipboard(formatted);
} catch (err) {
alert(err);
}
};

View File

@ -135,3 +135,50 @@ export function getDiff(obj1, obj2) {
}
return result;
}
/**
* `navigator.clipboard.writeText()`, but with an additional fallback for non-secure contexts.
*
* Never throws unless textPromise is rejected.
*/
export async function copyToClipboard(
textPromise: Promise<string>
): Promise<void> {
// Try the new clipboard APIs first. If that fails, use textarea fallback.
try {
await navigator.clipboard.write([
new ClipboardItem({
"text/plain": textPromise,
}),
]);
return;
} catch (err) {
console.warn(err);
}
let text = await textPromise;
try {
await navigator.clipboard.writeText(text);
return;
} catch (err) {
console.warn(err);
}
let t = document.createElement("textarea");
t.value = text;
t.style.position = "absolute";
t.style.opacity = "0";
document.body.appendChild(t);
try {
t.focus();
t.select();
if (!document.execCommand("copy")) {
throw "failed to copy";
}
} catch {
alert(text);
} finally {
t.remove();
}
}