Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
H
html2pdf
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Lukas Niegsch
html2pdf
Commits
2cc58b53
Commit
2cc58b53
authored
Jul 20, 2022
by
lukas.niegsch
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
finished dynamic api for methods
parent
67150aa0
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
193 additions
and
139 deletions
+193
-139
gadget_converter.html
gadget_converter.html
+2
-2
gadget_converter.js
gadget_converter.js
+34
-21
gadget_devtools.js
gadget_devtools.js
+157
-116
No files found.
gadget_converter.html
View file @
2cc58b53
...
...
@@ -9,8 +9,8 @@
</head>
<body>
<div
data-gadget-url=
"gadget_
browser
.html"
data-gadget-scope=
"
browser
"
data-gadget-url=
"gadget_
devtools
.html"
data-gadget-scope=
"
devtools
"
data-gadget-sandbox=
"public"
>
</div>
</body>
...
...
gadget_converter.js
View file @
2cc58b53
...
...
@@ -31,33 +31,46 @@
rJS
(
window
)
.
declareMethod
(
"
convert
"
,
function
(
html
)
{
var
gadget
=
this
;
var
browser
;
var
page
;
var
devtools
;
var
targetId
;
var
frameId
;
var
base64data
;
return
gadget
.
getDeclaredGadget
(
"
browser
"
)
/**
* Steps:
*
* // new page setup
* Target.createTarget();
* Target.attachToTarget();
* Target.activateTarget();
*
* // enable events
* Network.enable();
* Page.enable();
*
* // change content
* Page.frameAttached();
* Page.setDocumentContent();
* Page.loadEventFired(); // Is this event fired once loading starts or stops?
*
* // convert html to pdf
* Page.printToPDF();
*
* // cleanup
* Target.detachFromTarget();
* Target.closeTarget();
*/
return
gadget
.
getDeclaredGadget
(
"
devtools
"
)
.
push
(
function
(
subgadget
)
{
browser
=
subgadget
;
return
browser
.
openNewPage
(
"
http://www.example.com
"
);
return
subgadget
.
getDevtools
();
})
.
push
(
function
(
result
)
{
page
=
result
;
return
browser
.
setPageContent
(
page
,
html
);
})
.
push
(
function
()
{
return
browser
.
getVersion
();
})
.
push
(
function
()
{
return
browser
.
enablePage
(
page
);
})
.
push
(
function
()
{
return
browser
.
printToPdf
(
page
,
optionsForPrintToPdf
);
devtools
=
result
;
return
devtools
.
Browser
.
getVersion
();
})
.
push
(
function
(
result
)
{
base64data
=
result
;
return
browser
.
closePage
(
page
);
})
.
push
(
function
()
{
return
base64data
;
console
.
log
(
result
);
})
})
}(
window
,
rJS
));
\ No newline at end of file
gadget_devtools.js
View file @
2cc58b53
...
...
@@ -5,52 +5,6 @@ var BROWSER_PASSWORD = "ignored";
(
function
(
window
,
rJS
,
RSVP
)
{
"
use strict
"
;
/**
* The proxy to a specific browser target. Each proxy makes a connection
* to the targets websocket and handles the outgoing and incoming messages.
* It also automatically reconnects on network errors.
*
* TODO: maybe make this work with async or RSVP?
*/
class
TargetProxy
{
constructor
(
path
,
targetId
)
{
this
.
targetId
=
this
.
targetId
;
this
.
path
=
path
;
this
.
websocket
=
null
;
this
.
messages
=
[];
}
connect
(
callback
)
{
this
.
disconnect
();
this
.
websocket
=
new
WebSocket
(
this
.
path
);
this
.
websocket
.
onopen
=
callback
;
this
.
websocket
.
onmessage
=
this
.
messages
.
push
;
this
.
websocket
.
onerror
=
(
error
)
=>
{
console
.
error
(
error
);
this
.
connect
(
callback
);
};
}
disconnect
()
{
if
(
this
.
websocket
)
{
this
.
websocket
.
close
();
this
.
websocket
=
null
;
};
}
clearMessages
()
{
this
.
messages
=
[];
}
sendCommand
(
command
,
params
)
{
this
.
clearMessages
();
var
message
=
{
id
:
0
,
method
:
command
,
params
:
params
};
this
.
websocket
.
send
(
JSON
.
stringify
(
message
));
}
waitMessage
(
response
)
{
// TODO: implement this function
}
debug
()
{
console
.
log
(
this
.
messages
);
}
};
/**
* Makes an HTTP GET request to the browser to retrieve some JSON data.
*
...
...
@@ -72,46 +26,9 @@ var BROWSER_PASSWORD = "ignored";
request
.
send
();
}
return
RSVP
.
Queue
().
push
(
function
()
{
return
new
RSVP
.
Promise
(
callback
);
return
RSVP
.
Promise
(
callback
);
});
}
/**
* Adds the method API from the method JSON data to the object.
*
* @param {object} object The container for the method.
* @param {object} method The method data from the protocol.
*/
function
addMethodAPI
(
object
,
method
)
{
var
callback
=
(
proxy
,
params
)
=>
{
return
;
proxy
.
clearMessages
();
proxy
.
sendCommand
(
`
${
object
.
domain
}
.
${
method
.
name
}
`
,
params
);
}
//callback["description"] = method.description;
//callback["experimental"] = method.experimental ? true : false;
//callback["type"] = "method";
object
[
method
.
name
]
=
callback
;
}
/**
* Adds the event API from the event JSON data to the object.
*
* @param {object} object The container for the event.
* @param {object} event The event data from the protocol.
*/
function
addEventAPI
(
object
,
event
)
{
// todo
}
/**
* Adds the type API from the type JSON data to the object.
*
* @param {object} object The container for the type.
* @param {object} type The type data from the protocol.
*/
function
addTypeAPI
(
object
,
type
)
{
// todo
}
/**
* Returns the devtools API for the given domain. Domains are defined
* by the devtools protocol, e.g. Page. Each domain defines multiple
...
...
@@ -120,27 +37,75 @@ var BROWSER_PASSWORD = "ignored";
* @param {object} domain The domain data from the devtools API.
* @returns The promise for the devtools API object and its name.
*/
function
getDevtoolsAPI
(
domain
)
{
function
getDevtoolsAPI
(
domain
)
{
var
callback
=
(
resolve
)
=>
{
var
object
=
{};
//object["domain"] = domain.domain;
//object["description"] = domain.description;
//object["experimental"] = domain.experimental ? true : false;
(
domain
.
commands
||
[]).
forEach
((
method
)
=>
{
addMethodAPI
(
object
,
method
);
addMethodAPI
(
object
,
domain
,
method
);
});
(
domain
.
events
||
[]).
forEach
((
event
)
=>
{
addEventAPI
(
object
,
event
);
});
(
domain
.
types
||
[]).
forEach
((
type
)
=>
{
addTypeAPI
(
object
,
type
);
addEventAPI
(
object
,
domain
,
event
);
});
resolve
([
domain
.
domain
,
object
]);
}
return
RSVP
.
Queue
().
push
(
function
()
{
return
new
RSVP
.
Promise
(
callback
);
return
RSVP
.
Promise
(
callback
);
});
}
/**
* Adds the method API from the method JSON data to the object.
*
* @param {object} object The container for the method.
* @param {object} domain The domain data from the protocol.
* @param {object} method The method data from the protocol.
*/
function
addMethodAPI
(
object
,
domain
,
method
)
{
var
result
=
(
gadget
,
params
)
=>
{
var
callback
=
(
resolve
,
reject
)
=>
{
var
sessionId
=
gadget
.
state
.
sessionId
;
var
command
=
{
sessionId
,
id
:
gadget
.
state
.
nextIndex
,
method
:
`
${
domain
.
domain
}
.
${
method
.
name
}
`
,
params
:
params
,
}
var
message
=
JSON
.
stringify
(
command
);
gadget
.
state
.
websocket
.
send
(
message
);
var
handler
=
{
id
:
gadget
.
state
.
nextIndex
,
result
:
resolve
,
error
:
reject
}
gadget
.
state
.
handlers
.
add
(
handler
);
}
return
RSVP
.
Queue
().
push
(
function
()
{
return
RSVP
.
Promise
(
callback
);
});
}
object
[
method
.
name
]
=
result
;
}
/**
* Adds the event API from the method JSON data to the object.
*
* @param {object} object The container for the method.
* @param {object} domain The domain data from the protocol.
* @param {object} method The method data from the protocol.
*/
function
addEventAPI
(
object
,
domain
,
method
)
{
var
result
=
(
gadget
,
params
)
=>
{
var
callback
=
(
resolve
,
reject
)
=>
{
// todo: implement this method
resolve
(
null
);
}
return
RSVP
.
Queue
().
push
(
function
()
{
return
RSVP
.
Promise
(
callback
);
});
}
object
[
method
.
name
]
=
result
;
}
/**
* Wraps the devtools API inside the gadget state which handels the
* connection with specific targets.
...
...
@@ -148,49 +113,98 @@ var BROWSER_PASSWORD = "ignored";
* @param {*} gadget
* @param {*} devtoolsAPI
*/
function
getWrappedDevtoolsAPI
(
gadget
,
devtoolsAPI
)
{
var
callback
=
(
resolve
)
=>
{
// "map(partial(partial, proxy = gadget.proxy), leafs(devtoolsAPI))"
function
updateDevtools
(
gadget
,
devtoolsAPI
)
{
var
callback
=
()
=>
{
for
(
let
[
domain
,
methods
]
of
Object
.
entries
(
devtoolsAPI
))
{
for
(
let
[
method
,
callback
]
of
Object
.
entries
(
methods
))
{
devtoolsAPI
[
domain
][
method
]
=
(
params
)
=>
{
callback
(
gadget
.
proxy
,
params
);
return
callback
(
gadget
,
params
);
};
}
}
// TODO: special case for targets to make the abstraction work:
// TODO: attachTarget/detachFromTarget -> add/remove from target list
// TODO: activateTarget -> change current proxy target
console
.
log
(
devtoolsAPI
);
resolve
(
devtoolsAPI
);
let
attachToTarget
=
devtoolsAPI
.
Target
.
attachToTarget
;
devtoolsAPI
.
Target
.
attachToTarget
=
(
params
)
=>
{
var
response
;
return
attachToTarget
(
params
)
.
push
(
function
(
result
)
{
response
=
result
;
return
gadget
.
changeState
({
sessionId
:
response
.
sessionId
});
})
.
push
(
function
()
{
return
response
;
})
}
let
detachFromTarget
=
devtoolsAPI
.
Target
.
detachFromTarget
;
devtoolsAPI
.
Target
.
detachFromTarget
=
(
params
)
=>
{
var
response
;
return
detachFromTarget
(
params
)
.
push
(
function
(
result
)
{
response
=
result
;
return
gadget
.
changeState
({
sessionId
:
undefined
});
})
.
push
(
function
()
{
return
response
;
})
}
return
gadget
.
changeState
({
devtools
:
devtoolsAPI
});
}
return
RSVP
.
Queue
().
push
(
callback
);
}
/**
* Creates a new websocket that puts messages into the gadget's state.
* It also automatically reconnects on network errors.
*
* @param {object} gadget The gadget that should receive messages.
* @param {string} websocketUrl The url of the websocket.
*/
function
updateWebsocket
(
gadget
,
websocketUrl
)
{
var
callback
=
(
resolve
)
=>
{
var
websocket
=
new
WebSocket
(
websocketUrl
);
websocket
.
onopen
=
resolve
;
websocket
.
onmessage
=
(
message
)
=>
{
gadget
.
state
.
messages
.
add
(
JSON
.
parse
(
message
.
data
));
gadget
.
changeState
({
nextIndex
:
gadget
.
state
.
nextIndex
+
1
});
}
websocket
.
onerror
=
(
error
)
=>
{
console
.
log
(
error
);
websocket
.
close
();
updateWebsocket
(
gadget
,
websocketUrl
);
}
gadget
.
changeState
({
websocket
:
websocket
});
}
return
RSVP
.
Queue
().
push
(
function
()
{
return
new
RSVP
.
Promise
(
callback
);
return
RSVP
.
Promise
(
callback
);
});
}
rJS
(
window
)
.
setState
({
browser
:
null
,
targets
:
[],
proxy
:
null
,
websocket
:
null
,
sessionId
:
undefined
,
nextIndex
:
0
,
handlers
:
null
,
messages
:
null
,
devtools
:
{},
})
.
declareService
(
function
()
{
.
ready
(
function
()
{
var
gadget
=
this
;
return
getBrowserJSON
(
"
json/protocol
"
)
gadget
.
state
.
handlers
=
new
Set
();
gadget
.
state
.
messages
=
new
Set
();
return
getBrowserJSON
(
"
json/version
"
)
.
push
(
function
(
result
)
{
return
updateWebsocket
(
gadget
,
result
.
webSocketDebuggerUrl
);
})
.
push
(
function
()
{
return
getBrowserJSON
(
"
json/protocol
"
)
})
.
push
(
function
(
result
)
{
var
promises
=
result
.
domains
.
map
(
getDevtoolsAPI
);
return
RSVP
.
all
(
promises
);
})
.
push
(
function
(
result
)
{
var
devtools
=
devtools
=
Object
.
fromEntries
(
result
);
return
getWrappedDevtoolsAPI
(
gadget
,
devtools
);
return
updateDevtools
(
gadget
,
devtools
);
})
.
push
(
function
(
result
)
{
return
gadget
.
changeState
({
devtools
:
result
});
})
})
.
declareMethod
(
"
getDevtools
"
,
function
()
{
var
gadget
=
this
;
...
...
@@ -198,8 +212,35 @@ var BROWSER_PASSWORD = "ignored";
return
gadget
.
state
.
devtools
;
})
})
.
onStateChange
(
function
(
ignored
)
{
.
declareMethod
(
"
clearMessages
"
,
function
(
)
{
var
gadget
=
this
;
console
.
log
(
gadget
.
state
.
devtoolsAPI
);
gadget
.
state
.
messages
=
[];
})
.
onStateChange
(
function
(
modifications
)
{
var
gadget
=
this
;
/*
* This is O(n * m) with n = messages.size and m = handlers.size.
*
* I think it is fine since new messages should complete all handlers,
* which means m stays very small. We also have messages from events,
* but there are only finite amount, so n should stay small aswell.
*
* This can be implemented much faster if performance becomes an issue.
*/
for
(
let
i
=
gadget
.
state
.
handlers
.
values
(),
handler
=
null
;
handler
=
i
.
next
().
value
;)
{
for
(
let
j
=
gadget
.
state
.
messages
.
values
(),
message
=
null
;
message
=
j
.
next
().
value
;)
{
if
(
handler
.
id
!=
message
.
id
)
{
continue
;
}
gadget
.
state
.
handlers
.
delete
(
handler
);
gadget
.
state
.
messages
.
delete
(
message
);
if
(
'
error
'
in
message
)
{
handler
.
error
(
message
.
error
);
}
else
{
handler
.
result
(
message
.
result
)
}
}
}
})
}(
window
,
rJS
,
RSVP
));
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment