Home | History | Annotate | Download | only in applescript
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #import "chrome/browser/ui/cocoa/applescript/tab_applescript.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/files/file_path.h"
      9 #include "base/logging.h"
     10 #import "base/mac/scoped_nsobject.h"
     11 #include "base/strings/sys_string_conversions.h"
     12 #include "chrome/browser/printing/print_view_manager.h"
     13 #include "chrome/browser/sessions/session_tab_helper.h"
     14 #include "chrome/browser/ui/cocoa/applescript/apple_event_util.h"
     15 #include "chrome/browser/ui/cocoa/applescript/error_applescript.h"
     16 #include "chrome/common/url_constants.h"
     17 #include "components/sessions/session_id.h"
     18 #include "content/public/browser/navigation_controller.h"
     19 #include "content/public/browser/navigation_entry.h"
     20 #include "content/public/browser/render_frame_host.h"
     21 #include "content/public/browser/render_view_host.h"
     22 #include "content/public/browser/save_page_type.h"
     23 #include "content/public/browser/web_contents.h"
     24 #include "content/public/browser/web_contents_delegate.h"
     25 #include "url/gurl.h"
     26 
     27 using content::NavigationController;
     28 using content::NavigationEntry;
     29 using content::OpenURLParams;
     30 using content::RenderFrameHost;
     31 using content::RenderViewHost;
     32 using content::Referrer;
     33 using content::WebContents;
     34 
     35 namespace {
     36 
     37 void ResumeAppleEventAndSendReply(NSAppleEventManagerSuspensionID suspension_id,
     38                                   const base::Value* result_value) {
     39   NSAppleEventDescriptor* result_descriptor =
     40       chrome::mac::ValueToAppleEventDescriptor(result_value);
     41 
     42   NSAppleEventManager* manager = [NSAppleEventManager sharedAppleEventManager];
     43   NSAppleEventDescriptor* reply_event =
     44       [manager replyAppleEventForSuspensionID:suspension_id];
     45   [reply_event setParamDescriptor:result_descriptor
     46                        forKeyword:keyDirectObject];
     47   [manager resumeWithSuspensionID:suspension_id];
     48 }
     49 
     50 }  // namespace
     51 
     52 @interface TabAppleScript()
     53 @property (nonatomic, copy) NSString* tempURL;
     54 @end
     55 
     56 @implementation TabAppleScript
     57 
     58 @synthesize tempURL = tempURL_;
     59 
     60 - (id)init {
     61   if ((self = [super init])) {
     62     SessionID session;
     63     SessionID::id_type futureSessionIDOfTab = session.id() + 1;
     64     // Holds the SessionID that the new tab is going to get.
     65     base::scoped_nsobject<NSNumber> numID(
     66         [[NSNumber alloc] initWithInt:futureSessionIDOfTab]);
     67     [self setUniqueID:numID];
     68   }
     69   return self;
     70 }
     71 
     72 - (void)dealloc {
     73   [tempURL_ release];
     74   [super dealloc];
     75 }
     76 
     77 - (id)initWithWebContents:(content::WebContents*)webContents {
     78   if (!webContents) {
     79     [self release];
     80     return nil;
     81   }
     82 
     83   if ((self = [super init])) {
     84     // It is safe to be weak; if a tab goes away (e.g. the user closes a tab)
     85     // the AppleScript runtime calls tabs in AppleScriptWindow and this
     86     // particular tab is never returned.
     87     webContents_ = webContents;
     88     SessionTabHelper* session_tab_helper =
     89         SessionTabHelper::FromWebContents(webContents);
     90     base::scoped_nsobject<NSNumber> numID(
     91         [[NSNumber alloc] initWithInt:session_tab_helper->session_id().id()]);
     92     [self setUniqueID:numID];
     93   }
     94   return self;
     95 }
     96 
     97 - (void)setWebContents:(content::WebContents*)webContents {
     98   DCHECK(webContents);
     99   // It is safe to be weak; if a tab goes away (e.g. the user closes a tab)
    100   // the AppleScript runtime calls tabs in AppleScriptWindow and this
    101   // particular tab is never returned.
    102   webContents_ = webContents;
    103   SessionTabHelper* session_tab_helper =
    104       SessionTabHelper::FromWebContents(webContents);
    105   base::scoped_nsobject<NSNumber> numID(
    106       [[NSNumber alloc] initWithInt:session_tab_helper->session_id().id()]);
    107   [self setUniqueID:numID];
    108 
    109   if ([self tempURL])
    110     [self setURL:[self tempURL]];
    111 }
    112 
    113 - (NSString*)URL {
    114   if (!webContents_) {
    115     return nil;
    116   }
    117 
    118   NavigationEntry* entry = webContents_->GetController().GetActiveEntry();
    119   if (!entry) {
    120     return nil;
    121   }
    122   const GURL& url = entry->GetVirtualURL();
    123   return base::SysUTF8ToNSString(url.spec());
    124 }
    125 
    126 - (void)setURL:(NSString*)aURL {
    127   // If a scripter sets a URL before the node is added save it at a temporary
    128   // location.
    129   if (!webContents_) {
    130     [self setTempURL:aURL];
    131     return;
    132   }
    133 
    134   GURL url(base::SysNSStringToUTF8(aURL));
    135   // check for valid url.
    136   if (!url.is_empty() && !url.is_valid()) {
    137     AppleScript::SetError(AppleScript::errInvalidURL);
    138     return;
    139   }
    140 
    141   NavigationEntry* entry = webContents_->GetController().GetActiveEntry();
    142   if (!entry)
    143     return;
    144 
    145   const GURL& previousURL = entry->GetVirtualURL();
    146   webContents_->OpenURL(OpenURLParams(
    147       url,
    148       content::Referrer(previousURL, blink::WebReferrerPolicyDefault),
    149       CURRENT_TAB,
    150       ui::PAGE_TRANSITION_TYPED,
    151       false));
    152 }
    153 
    154 - (NSString*)title {
    155   NavigationEntry* entry = webContents_->GetController().GetActiveEntry();
    156   if (!entry)
    157     return nil;
    158 
    159   base::string16 title = entry ? entry->GetTitle() : base::string16();
    160   return base::SysUTF16ToNSString(title);
    161 }
    162 
    163 - (NSNumber*)loading {
    164   BOOL loadingValue = webContents_->IsLoading() ? YES : NO;
    165   return [NSNumber numberWithBool:loadingValue];
    166 }
    167 
    168 - (void)handlesUndoScriptCommand:(NSScriptCommand*)command {
    169   webContents_->Undo();
    170 }
    171 
    172 - (void)handlesRedoScriptCommand:(NSScriptCommand*)command {
    173   webContents_->Redo();
    174 }
    175 
    176 - (void)handlesCutScriptCommand:(NSScriptCommand*)command {
    177   webContents_->Cut();
    178 }
    179 
    180 - (void)handlesCopyScriptCommand:(NSScriptCommand*)command {
    181   webContents_->Copy();
    182 }
    183 
    184 - (void)handlesPasteScriptCommand:(NSScriptCommand*)command {
    185   webContents_->Paste();
    186 }
    187 
    188 - (void)handlesSelectAllScriptCommand:(NSScriptCommand*)command {
    189   webContents_->SelectAll();
    190 }
    191 
    192 - (void)handlesGoBackScriptCommand:(NSScriptCommand*)command {
    193   NavigationController& navigationController = webContents_->GetController();
    194   if (navigationController.CanGoBack())
    195     navigationController.GoBack();
    196 }
    197 
    198 - (void)handlesGoForwardScriptCommand:(NSScriptCommand*)command {
    199   NavigationController& navigationController = webContents_->GetController();
    200   if (navigationController.CanGoForward())
    201     navigationController.GoForward();
    202 }
    203 
    204 - (void)handlesReloadScriptCommand:(NSScriptCommand*)command {
    205   NavigationController& navigationController = webContents_->GetController();
    206   const bool checkForRepost = true;
    207   navigationController.Reload(checkForRepost);
    208 }
    209 
    210 - (void)handlesStopScriptCommand:(NSScriptCommand*)command {
    211   webContents_->Stop();
    212 }
    213 
    214 - (void)handlesPrintScriptCommand:(NSScriptCommand*)command {
    215   bool initiated =
    216       printing::PrintViewManager::FromWebContents(webContents_)->PrintNow();
    217   if (!initiated) {
    218     AppleScript::SetError(AppleScript::errInitiatePrinting);
    219   }
    220 }
    221 
    222 - (void)handlesSaveScriptCommand:(NSScriptCommand*)command {
    223   NSDictionary* dictionary = [command evaluatedArguments];
    224 
    225   NSURL* fileURL = [dictionary objectForKey:@"File"];
    226   // Scripter has not specifed the location at which to save, so we prompt for
    227   // it.
    228   if (!fileURL) {
    229     webContents_->OnSavePage();
    230     return;
    231   }
    232 
    233   base::FilePath mainFile(base::SysNSStringToUTF8([fileURL path]));
    234   // We create a directory path at the folder within which the file exists.
    235   // Eg.    if main_file = '/Users/Foo/Documents/Google.html'
    236   // then directory_path = '/Users/Foo/Documents/Google_files/'.
    237   base::FilePath directoryPath = mainFile.RemoveExtension();
    238   directoryPath = directoryPath.InsertBeforeExtension(std::string("_files/"));
    239 
    240   NSString* saveType = [dictionary objectForKey:@"FileType"];
    241 
    242   content::SavePageType savePageType = content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML;
    243   if (saveType) {
    244     if ([saveType isEqualToString:@"only html"]) {
    245       savePageType = content::SAVE_PAGE_TYPE_AS_ONLY_HTML;
    246     } else if ([saveType isEqualToString:@"complete html"]) {
    247       savePageType = content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML;
    248     } else {
    249       AppleScript::SetError(AppleScript::errInvalidSaveType);
    250       return;
    251     }
    252   }
    253 
    254   webContents_->SavePage(mainFile, directoryPath, savePageType);
    255 }
    256 
    257 - (void)handlesCloseScriptCommand:(NSScriptCommand*)command {
    258   webContents_->GetDelegate()->CloseContents(webContents_);
    259 }
    260 
    261 - (void)handlesViewSourceScriptCommand:(NSScriptCommand*)command {
    262   NavigationEntry* entry =
    263       webContents_->GetController().GetLastCommittedEntry();
    264   if (entry) {
    265     webContents_->OpenURL(
    266         OpenURLParams(GURL(content::kViewSourceScheme + std::string(":") +
    267                            entry->GetURL().spec()),
    268                       Referrer(),
    269                       NEW_FOREGROUND_TAB,
    270                       ui::PAGE_TRANSITION_LINK,
    271                       false));
    272   }
    273 }
    274 
    275 - (id)handlesExecuteJavascriptScriptCommand:(NSScriptCommand*)command {
    276   content::RenderFrameHost* frame = webContents_->GetMainFrame();
    277   if (!frame) {
    278     NOTREACHED();
    279     return nil;
    280   }
    281 
    282   NSAppleEventManager* manager = [NSAppleEventManager sharedAppleEventManager];
    283   NSAppleEventManagerSuspensionID suspensionID =
    284       [manager suspendCurrentAppleEvent];
    285   content::RenderFrameHost::JavaScriptResultCallback callback =
    286       base::Bind(&ResumeAppleEventAndSendReply, suspensionID);
    287 
    288   base::string16 script = base::SysNSStringToUTF16(
    289       [[command evaluatedArguments] objectForKey:@"javascript"]);
    290   frame->ExecuteJavaScript(script, callback);
    291 
    292   return nil;
    293 }
    294 
    295 @end
    296