WebKit on 10.8: How to programmatically set the value of an input field [type = file]?

Setup :

  • Mac OS X 10.8.2
  • A normal Cocoa application that references the version of WebKit provided by the OS. Specifically, the contents: /System/Library/Frameworks/WebKit.framework/Versions/A/Resources/version.plist :
  <plist version = "1.0">
     <dict>
         <key> BuildVersion </key>
         <string> 5 </string>
         <key> CFBundleShortVersionString </key>
         <string> 8536 </string>
         <key> CFBundleVersion </key>
         <string> 8536.26.14 </string>
         <key> ProjectName </key>
         <string> WebKit </string>
         <key> SourceVersion </key>
         <string> 7536026014000000 </string>
     </dict>
     </plist>

I have a WebKit-based Cocoa application that loads an HTML document containing a normal HTML form into a WebView. The HTML form contains a file selection input field similar to this:

 <form name="foo"> <input type="file" name="bar"> </form> 

I would like to set the value of this file selection programmatically (from Objective-C, if possible, but I will do everything that works).

As far as I can tell, this method never worked:

 DOMHTMLInputElement *inputEl = ... // fetch input element [inputEl setValue:@"some/file.txt"]; 

I assume that in WebKit there is a security restriction / policy that prevents this from working. I assume this is a deliberate security feature in WebKit, not a bug.


However, in OS X 10.7 Lion, I was able to get around this limitation with a little hack.

In Lion, you can programmatically push the input element:

 #pragma mark - #pragma mark WebFrameLoadDelegate - (void)webView:(WebView *)wv didFinishLoadForFrame:(WebFrame *)frame { if (frame != [wv mainFrame]) return; DOMAbstractView *win = (id)[frame windowObject]; DOMDocument *doc = [win document]; DOMHTMLFormElement *formEl = (id)[[doc forms] namedItem:@"foo"]; DOMHTMLInputElement *inputEl = (id)[[formEl elements] namedItem:@"bar"]; [inputEl click]; } 

What will cause the call -[WebUIDelegate webView:runOpenPanelForFileButtonWithResultListener:] . You can then implement this delegate method to programmatically set the value of the recipient immediately:

 #pragma mark - #pragma mark WebUIDelegate - (void)webView:(WebView *)wv runOpenPanelForFileButtonWithResultListener:(id<WebOpenPanelResultListener>)listener { [listener chooseFilename:@"some/file.txt"]; } 

Although it was an ugly hack, it worked just fine. It had the desired effect by directly setting the value of the input file's load element. The "open panel" will not appear on the screen.


I am afraid that WebKit has stopped allowing this in the version shipped with 10.8. Modified part:

 [inputEl click]; 

This no longer generates a click event for an element in WebKit delivery since 10.8.

I tried other push methods that worked on 10.7, but no longer work on 10.8:

 DOMUIEvent *evt = (id)[doc createEvent:@"UIEvents"]; [evt initUIEvent:@"click" canBubble:YES cancelable:YES view:win detail:1]; [inputEl dispatchEvent:evt]; 

These methods worked in 10.7, but none of them work in 10.8.


So. Is there a way to programmatically set the value of this file to 10.8?

NOTE I do NOT want to send a custom version of WebKit with my application. Other than that, I am open to any suggestion (ObjC or JS or something else).

How can you programmatically set the file selection value in WebKit, which comes with 10.8?

I have a test project example (a smaller test case) available for your convenience here: http://tod.nu/FileUploadTest.zip

+4
source share
1 answer

This will work if you send an event through the AppKit event system. Sort of:

 NSView *docView = [[[webView mainFrame] frameView] documentView]; NSRect docFrame = [docView frame]; NSPoint point = [el boundingBox].origin; point.y = docFrame.size.height - point.y; NSEvent *evt = [NSEvent mouseEventWithType:NSLeftMouseDown location:point modifierFlags:0 timestamp:[[NSDate date] timeIntervalSinceReferenceDate] windowNumber:[self.webView.window windowNumber] context:0 eventNumber:0 clickCount:1 pressure:0]; [self.webView.window sendEvent:evt]; evt = [NSEvent mouseEventWithType:NSLeftMouseUp location:point modifierFlags:0 timestamp:[[NSDate date] timeIntervalSinceReferenceDate] windowNumber:[self.webView.window windowNumber] context:0 eventNumber:0 clickCount:1 pressure:0]; [self.webView.window sendEvent:evt]; 

Probably the best way to do a coordinate system transformation, but this is not a very important part.

+7
source

All Articles