2019-12-24 18:47:21 -05:00
|
|
|
package dom
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-11-23 20:12:04 -05:00
|
|
|
"sync"
|
|
|
|
|
2020-07-13 14:13:03 -04:00
|
|
|
"github.com/mafredri/cdp"
|
2019-12-24 18:47:21 -05:00
|
|
|
"github.com/mafredri/cdp/protocol/page"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/rs/zerolog"
|
2020-11-23 20:12:04 -05:00
|
|
|
|
|
|
|
"github.com/MontFerret/ferret/pkg/drivers/cdp/input"
|
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
2019-12-24 18:47:21 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
|
|
|
Manager struct {
|
2020-08-25 01:25:07 -04:00
|
|
|
mu sync.RWMutex
|
2019-12-24 18:47:21 -05:00
|
|
|
logger *zerolog.Logger
|
|
|
|
client *cdp.Client
|
|
|
|
mouse *input.Mouse
|
|
|
|
keyboard *input.Keyboard
|
2020-07-13 14:13:03 -04:00
|
|
|
mainFrame *AtomicFrameID
|
|
|
|
frames *AtomicFrameCollection
|
2019-12-24 18:47:21 -05:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
func New(
|
|
|
|
logger *zerolog.Logger,
|
|
|
|
client *cdp.Client,
|
|
|
|
mouse *input.Mouse,
|
|
|
|
keyboard *input.Keyboard,
|
|
|
|
) (manager *Manager, err error) {
|
|
|
|
|
|
|
|
manager = new(Manager)
|
|
|
|
manager.logger = logger
|
|
|
|
manager.client = client
|
|
|
|
manager.mouse = mouse
|
|
|
|
manager.keyboard = keyboard
|
2020-07-13 14:13:03 -04:00
|
|
|
manager.mainFrame = NewAtomicFrameID()
|
|
|
|
manager.frames = NewAtomicFrameCollection()
|
2020-07-09 21:25:58 -04:00
|
|
|
|
2019-12-24 18:47:21 -05:00
|
|
|
return manager, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) Close() error {
|
2020-07-13 14:13:03 -04:00
|
|
|
errs := make([]error, 0, m.frames.Length()+1)
|
2020-07-09 21:25:58 -04:00
|
|
|
|
2020-07-13 14:13:03 -04:00
|
|
|
m.frames.ForEach(func(f Frame, key page.FrameID) bool {
|
2019-12-24 18:47:21 -05:00
|
|
|
// if initialized
|
|
|
|
if f.node != nil {
|
|
|
|
if err := f.node.Close(); err != nil {
|
|
|
|
errs = append(errs, err)
|
|
|
|
}
|
|
|
|
}
|
2020-07-13 14:13:03 -04:00
|
|
|
|
|
|
|
return true
|
|
|
|
})
|
2019-12-24 18:47:21 -05:00
|
|
|
|
|
|
|
if len(errs) > 0 {
|
|
|
|
return core.Errors(errs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) GetMainFrame() *HTMLDocument {
|
2020-08-25 01:25:07 -04:00
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
2020-07-13 14:13:03 -04:00
|
|
|
mainFrameID := m.mainFrame.Get()
|
2019-12-24 18:47:21 -05:00
|
|
|
|
2020-07-13 14:13:03 -04:00
|
|
|
if mainFrameID == "" {
|
2019-12-24 18:47:21 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-13 14:13:03 -04:00
|
|
|
mainFrame, exists := m.frames.Get(mainFrameID)
|
2019-12-24 18:47:21 -05:00
|
|
|
|
|
|
|
if exists {
|
|
|
|
return mainFrame.node
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) SetMainFrame(doc *HTMLDocument) {
|
2020-08-25 01:25:07 -04:00
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
|
2020-07-13 14:13:03 -04:00
|
|
|
mainFrameID := m.mainFrame.Get()
|
2019-12-24 18:47:21 -05:00
|
|
|
|
2020-07-13 14:13:03 -04:00
|
|
|
if mainFrameID != "" {
|
|
|
|
if err := m.removeFrameRecursivelyInternal(mainFrameID); err != nil {
|
2019-12-24 18:47:21 -05:00
|
|
|
m.logger.Error().Err(err).Msg("failed to close previous main frame")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-13 14:13:03 -04:00
|
|
|
m.mainFrame.Set(doc.frameTree.Frame.ID)
|
2019-12-24 18:47:21 -05:00
|
|
|
|
|
|
|
m.addPreloadedFrame(doc)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) AddFrame(frame page.FrameTree) {
|
2020-08-25 01:25:07 -04:00
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
2019-12-24 18:47:21 -05:00
|
|
|
m.addFrameInternal(frame)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) RemoveFrame(frameID page.FrameID) error {
|
2020-08-25 01:25:07 -04:00
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
2019-12-24 18:47:21 -05:00
|
|
|
return m.removeFrameInternal(frameID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) RemoveFrameRecursively(frameID page.FrameID) error {
|
2020-08-25 01:25:07 -04:00
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
2019-12-24 18:47:21 -05:00
|
|
|
return m.removeFrameRecursivelyInternal(frameID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) RemoveFramesByParentID(parentFrameID page.FrameID) error {
|
2020-08-25 01:25:07 -04:00
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
2020-07-13 14:13:03 -04:00
|
|
|
frame, found := m.frames.Get(parentFrameID)
|
2019-12-24 18:47:21 -05:00
|
|
|
|
|
|
|
if !found {
|
|
|
|
return errors.New("frame not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, child := range frame.tree.ChildFrames {
|
|
|
|
if err := m.removeFrameRecursivelyInternal(child.Frame.ID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) GetFrameNode(ctx context.Context, frameID page.FrameID) (*HTMLDocument, error) {
|
|
|
|
return m.getFrameInternal(ctx, frameID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) GetFrameTree(_ context.Context, frameID page.FrameID) (page.FrameTree, error) {
|
2020-08-25 01:25:07 -04:00
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
2020-07-13 14:13:03 -04:00
|
|
|
frame, found := m.frames.Get(frameID)
|
2019-12-24 18:47:21 -05:00
|
|
|
|
|
|
|
if !found {
|
|
|
|
return page.FrameTree{}, core.ErrNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
return frame.tree, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) GetFrameNodes(ctx context.Context) (*values.Array, error) {
|
2020-08-25 01:25:07 -04:00
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
2020-07-13 14:13:03 -04:00
|
|
|
arr := values.NewArray(m.frames.Length())
|
2019-12-24 18:47:21 -05:00
|
|
|
|
2020-07-13 14:13:03 -04:00
|
|
|
for _, f := range m.frames.ToSlice() {
|
2019-12-24 18:47:21 -05:00
|
|
|
doc, err := m.getFrameInternal(ctx, f.tree.Frame.ID)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
arr.Push(doc)
|
|
|
|
}
|
|
|
|
|
|
|
|
return arr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) addFrameInternal(frame page.FrameTree) {
|
2020-07-13 14:13:03 -04:00
|
|
|
m.frames.Set(frame.Frame.ID, Frame{
|
2019-12-24 18:47:21 -05:00
|
|
|
tree: frame,
|
|
|
|
node: nil,
|
2020-07-13 14:13:03 -04:00
|
|
|
})
|
2019-12-24 18:47:21 -05:00
|
|
|
|
|
|
|
for _, child := range frame.ChildFrames {
|
|
|
|
m.addFrameInternal(child)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) addPreloadedFrame(doc *HTMLDocument) {
|
2020-07-13 14:13:03 -04:00
|
|
|
m.frames.Set(doc.frameTree.Frame.ID, Frame{
|
2019-12-24 18:47:21 -05:00
|
|
|
tree: doc.frameTree,
|
|
|
|
node: doc,
|
2020-07-13 14:13:03 -04:00
|
|
|
})
|
2019-12-24 18:47:21 -05:00
|
|
|
|
|
|
|
for _, child := range doc.frameTree.ChildFrames {
|
|
|
|
m.addFrameInternal(child)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) getFrameInternal(ctx context.Context, frameID page.FrameID) (*HTMLDocument, error) {
|
2020-07-13 14:13:03 -04:00
|
|
|
frame, found := m.frames.Get(frameID)
|
2019-12-24 18:47:21 -05:00
|
|
|
|
|
|
|
if !found {
|
|
|
|
return nil, core.ErrNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
// frame is initialized
|
|
|
|
if frame.node != nil {
|
|
|
|
return frame.node, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// the frames is not loaded yet
|
|
|
|
node, execID, err := resolveFrame(ctx, m.client, frameID)
|
|
|
|
|
|
|
|
if err != nil {
|
2020-07-13 14:13:03 -04:00
|
|
|
return nil, errors.Wrapf(err, "failed to resolve frame node: %s", frameID)
|
2019-12-24 18:47:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
doc, err := LoadHTMLDocument(
|
|
|
|
ctx,
|
|
|
|
m.logger,
|
|
|
|
m.client,
|
|
|
|
m,
|
|
|
|
m.mouse,
|
|
|
|
m.keyboard,
|
|
|
|
node,
|
|
|
|
frame.tree,
|
|
|
|
execID,
|
|
|
|
)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to load frame document")
|
|
|
|
}
|
|
|
|
|
|
|
|
frame.node = doc
|
|
|
|
|
|
|
|
return doc, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) removeFrameInternal(frameID page.FrameID) error {
|
2020-07-13 14:13:03 -04:00
|
|
|
current, exists := m.frames.Get(frameID)
|
2019-12-24 18:47:21 -05:00
|
|
|
|
|
|
|
if !exists {
|
|
|
|
return core.Error(core.ErrNotFound, "frame")
|
|
|
|
}
|
|
|
|
|
2020-07-13 14:13:03 -04:00
|
|
|
m.frames.Remove(frameID)
|
|
|
|
|
|
|
|
mainFrameID := m.mainFrame.Get()
|
2019-12-24 18:47:21 -05:00
|
|
|
|
2020-07-13 14:13:03 -04:00
|
|
|
if frameID == mainFrameID {
|
|
|
|
m.mainFrame.Reset()
|
2019-12-24 18:47:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if current.node == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return current.node.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) removeFrameRecursivelyInternal(frameID page.FrameID) error {
|
2020-07-13 14:13:03 -04:00
|
|
|
parent, exists := m.frames.Get(frameID)
|
2019-12-24 18:47:21 -05:00
|
|
|
|
|
|
|
if !exists {
|
|
|
|
return core.Error(core.ErrNotFound, "frame")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, child := range parent.tree.ChildFrames {
|
|
|
|
if err := m.removeFrameRecursivelyInternal(child.Frame.ID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return m.removeFrameInternal(frameID)
|
|
|
|
}
|