Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A fourth composite pull request #80

Merged
merged 14 commits into from
Oct 15, 2019
Merged

A fourth composite pull request #80

merged 14 commits into from
Oct 15, 2019

Conversation

hmdne
Copy link
Member

@hmdne hmdne commented Oct 3, 2019

I would particularly like to bring some attention to this commit: 7781a2d. If someone has a better idea about where such a code can be located (or how it can be formed better), I would be glad to know.

It's like Browser#after, but works with promises. Allows you to
combine your timers with other promise code.
Now, this commit may be quite controversial and if some of you find
it too controversial, I would gladly hear from you.

I worked on Iframe#content_window, which sometimes returns a weird
object. It's a DOM node, but it's "Restricted", you can't edit its
properties, but it has a few features that you can use, like a
postMessage method.

None of the Native::Wrapper or Browser::NativeCachedWrapper could
work with such a node, because they checked a DOM node with a
Kernel#native? method - which checks for `#@Native.$$class` - this
triggers a SecurityError. Therefore if we could catch such an
exception... maybe we could treat this kind of object in a special
way?

Below is a sample code which can create such an object:

```
z = DOM { iframe sandbox: "" }
z.append_to($document.body)
z.content_window
```

This workaround uses Browser::NativeCachedWrapper. If it detects,
that a native object that is about to be wrapped is restricted, it
skips all other checks and creates a limited object. It works,
because we migrated all DOM to use NativeCachedWrapper. Of course,
such objects aren't cached anymore (well, it's not anymore - they
never were, because they never worked), but it may be possible to
cache them with a different approach, maybe further justifying the
location of this workaround...
This patch implements what's needed to support File input from
multiple sources (copy&paste, drag&drop, input[type=file]).

This patch also implements `DataTransferItem` as
`Browser::Event::DataTransfer::Item` which can be useful to handle
drag&drop inside a web application.

I used the following code to test:

```ruby
$document.ready do
  DOM { input type: "file", multiple: true; div.fd! { div } }.append_to($document.body)

  def show_files(e, method, files=nil)
    e.prevent
    items = []
    if e.respond_to?(:transfer) && files.nil?
      files = e.transfer.files
      items = e.transfer.items
    end

    p [e.class, method, files.count, items.count]

    z = $document.at_css('#fd>div')
    z.replace_with DOM { |b|
      b.div {
        b.div "method: #{method}"

        # This is a demo for DataTransfer#files
        files.each do |f|
          b.div(style: "border: 3px solid black") {
            b.div "filename: #{f.name}"
            b.div "mime type: #{f.type}"
            b.div "file size: #{f.size}"
            b.div "last modified: #{f.last_modified.to_s}"
            b.div { b.img src: f.to_url, style: "width: 300px" }
          }
        end

        # This is a demo for DataTransfer#items
        items.each do |i|
          if i.file?
            f = i.to_file
            b.div(style: "border: 3px solid blue") {
              b.div "filename: #{f.name}"
              b.div "mime type: #{f.type}"
              b.div "file size: #{f.size}"
              b.div "last modified: #{f.last_modified.to_s}"
              b.div { b.img src: f.to_url, style: "width: 300px" }
            }
          elsif i.string?
            b.div(style: "border: 3px solid lime") {
              b.div "type: #{i.type}"
            }
            # It's a weird API that can't return ASAP
            i.to_string.then { |s| `console.log(#{s})` }
          else
            b.div(style: "border: 3px solid yellow") {
              b.div "unknown kind: #{i.kind}"
              b.div "type: #{i.type}"
            }
          end
        end
      }
    }
  end

  input_file = $document.at_css('input[type="file"]')

  $window   .on("paste")     { |e| show_files(e, :paste) }

  $document .on("dragover")  { |e| show_files(e, :dragover) } # Must be e.prevented for this to work
  #$document .on("dragenter") { |e| show_files(e, :dragenter) } <-._ ^-._
  #$document .on("dragleave") { |e| show_files(e, :dragleave) } <-------------- Those 3 won't give you files
  $document .on("drop")      { |e| show_files(e, :drop) }

  input_file.on("change")    { |e| show_files(e, :input, input_file.files) }
end
```
* HTTP::Request now supports file upload via a newly implemented FormData
  interface.
* Unify query string generation. Move everything under a FormData class,
  deprecate old methods defined in opal/browser/utils.
* Support Rack and PHP compatible nested query strings.
  Fixes opal#33.
* Implement DOM::Element::Form. Support FormData generation from forms.
Copy link
Member

@elia elia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this PR, great stuff and actually super happy to see opal-browser coming back to life!

I left some comments, mostly questions or suggestions, with a couple of nits 👍

#
# @return [Browser::DOM::Element] the created `<style>` element
def CSS(*args, &block)
document = args.pop || $document
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this, I think, needs to be solved in another way, what if I just pass the css text as first argument… wouldn't it pop the last element from args which happens to also be the first one?

I'd suggest def CSS(text = nil, document = $document, &block), or a check to see if the argument is a String, or —maybe better— use the if block check as it's done in DOM(…).

end
end

end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EOF newline

# receive it.
def text(&block)
promise = nil
if ! block
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason for not using unless block_given? here?

def <<(tuple)
key, value, filename = tuple

if ! filename
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here and elsewhere, any reason for not using unless?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A personal preference, I tend to evade unless and until. In those cases unless may be a good choice.

obj = allocate
obj.instance_variable_set :@native, native
return obj
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About this I would suggest to move all the restricted logic here, and do the check within x-strings, catching just the security error instead of a generic JS::Error. This will make things faster, allow unrelated errors to correctly pop up, and keep this specific code inside NativeCachedWrapperClassMethods until a more general way of dealing with this kind of problem is found.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think about expanding the scope of that someday in the future.

It would be nice to have something similar to Native(`[]`). Like Browser.native(`iframe.contentWindow`) which would return an element of type Browser::Window.

This would probably involve using Object.getPrototypeOf and it could allow us to drop a warning if types don't match.

The previous patch missed a call to Kernel#CSS of a form:
`CSS(text)` in which case it treated `text` as a Document instance.
This commit also adds a test and moves things around as per @elia's
suggestion.
@hmdne
Copy link
Member Author

hmdne commented Oct 15, 2019

Thank you for your review. I do appreciate your input a lot :)

Please feel free to merge this PR now or later.

Copy link
Member

@elia elia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your review. I do appreciate your input a lot :)

I'm happy to hear that! I do my best to give it enough time to make it valuable in some way 👍👍👍

About using unless consider also this:

When dealing with library code it's always good to have a special regard for performance, in this case using if ! results in one additional method call to (Object#!@) that can become significant the moment this code is used while iterating through many elements.

foo if ! bar; nil

becomes:

  if ($truthy(self.$bar()['$!']())) {
    self.$foo()};
  return nil;

@elia elia merged commit a86f750 into opal:master Oct 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants