<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
+ <key>com.apple.security.files.bookmarks.app-scope</key>
+ <true/>
+ <key>com.apple.security.files.user-selected.read-only</key>
+ <true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
{
[self computeLineIndex];
- // Determine the width of the ruler based on the line count.
- NSUInteger lastElement = lineIndex_.back() + 1;
- NSAttributedString* lastElementString = [self attributedStringForLineNumber:lastElement];
- NSSize boundingSize = [lastElementString size];
- [self setRuleThickness:std::max(kDefaultWidth, boundingSize.width)];
+ if (lineIndex_.empty()) {
+ [self setRuleThickness:kDefaultWidth];
+ } else {
+ // Determine the width of the ruler based on the line count.
+ NSUInteger lastElement = lineIndex_.back() + 1;
+ NSAttributedString* lastElementString = [self attributedStringForLineNumber:lastElement];
+ NSSize boundingSize = [lastElementString size];
+ [self setRuleThickness:std::max(kDefaultWidth, boundingSize.width)];
+ }
[self setNeedsDisplay:YES];
}
index = NSMaxRange([text lineRangeForRange:NSMakeRange(index, 0)]);
}
+ if (lineIndex_.empty())
+ return;
+
NSUInteger lineEnd, contentEnd;
[text getLineStart:NULL
end:&lineEnd
// kBreakpointTypeFile:
@property (readonly) NSString* file;
@property (readonly) unsigned long line;
+@property (copy) NSData* secureBookmark;
// kBreakpointTypeFunctionEntry:
@property (readonly) NSString* functionName;
// Creates a dictionary representation for use in NSUserDefaults.
- (NSDictionary*)dictionary;
+// For kBreakpointTypeFile: ////////////////////////////////////////////////////
+
+// Creates a new secure bookmark for maintaining access to the file in the App
+// Sandbox across relaunches.
+- (BOOL)createSecureBookmark;
+
+// Call to enable read-only access to the file.
+- (BOOL)startSecureFileAccess;
+
+// Call when done accessing the file.
+- (BOOL)stopSecureFileAccess;
+
@end
unsigned long _debuggerId;
NSString* _file;
+ NSData* _secureBookmark;
+ NSURL* _secureFileAccess;
NSString* _functionName;
}
_type = kBreakpointTypeFile;
_file = [[dict valueForKey:@"file"] copy];
_line = [[dict valueForKey:@"line"] intValue];
+ _secureBookmark = [[dict valueForKey:@"secureBookmark"] copy];
} else if ([type isEqualToString:kBreakpointTypeFunctionEntry]) {
_type = kBreakpointTypeFunctionEntry;
_functionName = [[dict valueForKey:@"function"] copy];
return self;
}
+- (void)dealloc {
+ if (_secureFileAccess)
+ [self stopSecureFileAccess];
+}
+
/**
* Returns the string to display in the breakpoints list.
*/
- (NSDictionary*)dictionary
{
if (self.type == kBreakpointTypeFile) {
- return @{
+ NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithDictionary:@{
@"type" : self.type,
@"file" : self.file,
- @"line" : @(self.line)
- };
+ @"line" : @(self.line),
+ }];
+ if (self.secureBookmark)
+ [dict setObject:self.secureBookmark forKey:@"secureBookmark"];
+ return dict;
} else if (self.type == kBreakpointTypeFunctionEntry) {
return @{
@"type" : self.type,
return nil;
}
+- (BOOL)createSecureBookmark
+{
+ NSURL* fileURL = [NSURL fileURLWithPath:self.file];
+ return [self _createSecureBookmarkWithURL:fileURL];
+}
+
+- (BOOL)_createSecureBookmarkWithURL:(NSURL*)url
+{
+ NSError* error;
+ NSData* secureBookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope | NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess
+ includingResourceValuesForKeys:nil
+ relativeToURL:nil
+ error:&error];
+ if (secureBookmark) {
+ self.secureBookmark = secureBookmark;
+ return YES;
+ } else {
+ NSLog(@"Failed to create secure bookmark: %@", error);
+ return NO;
+ }
+}
+
+- (BOOL)startSecureFileAccess
+{
+ assert(self.type == kBreakpointTypeFile);
+ if (_secureFileAccess)
+ return YES;
+ if (!_secureBookmark)
+ return NO;
+
+ BOOL isStale;
+ NSError* error;
+ _secureFileAccess = [NSURL URLByResolvingBookmarkData:_secureBookmark
+ options:NSURLBookmarkResolutionWithSecurityScope
+ relativeToURL:nil
+ bookmarkDataIsStale:&isStale
+ error:&error];
+ if (error) {
+ NSLog(@"Failed to access file via secure bookmark: %@", error);
+ return NO;
+ }
+ if (isStale)
+ [self _createSecureBookmarkWithURL:_secureFileAccess];
+
+ return [_secureFileAccess startAccessingSecurityScopedResource];
+}
+
+- (BOOL)stopSecureFileAccess
+{
+ assert(self.type == kBreakpointTypeFile);
+ if (!_secureFileAccess)
+ return YES;
+ if (!_secureBookmark)
+ return NO;
+
+ [_secureFileAccess stopAccessingSecurityScopedResource];
+ _secureFileAccess = nil;
+ return YES;
+}
+
- (NSString*)description
{
return [NSString stringWithFormat:@"Breakpoint %@", [[self dictionary] description]];
@implementation BreakpointController {
BreakpointManager* _manager;
+ Breakpoint* _selection;
BSSourceView* _sourceView;
*/
- (void)tableViewSelectionDidChange:(NSNotification*)notif
{
+ if (_selection.type == kBreakpointTypeFile) {
+ [_selection stopSecureFileAccess];
+ _selection = nil;
+ }
+
NSArray* selection = [_arrayController selectedObjects];
if ([selection count] < 1) {
+ [_sourceView setString:@"" asFile:nil];
+ [_sourceView setMarkers:[NSSet set]];
return;
}
-
- Breakpoint* bp = [selection objectAtIndex:0];
- if (bp.type != kBreakpointTypeFile) {
+
+ _selection = [selection objectAtIndex:0];
+ if (_selection.type != kBreakpointTypeFile) {
return;
}
- [_sourceView setFile:[bp file]];
- [_sourceView scrollToLine:[bp line]];
- [_sourceView setMarkers:[_manager breakpointsForFile:bp.file]];
+ [_selection startSecureFileAccess];
+
+ [_sourceView setFile:[_selection file]];
+ [_sourceView scrollToLine:[_selection line]];
+ [_sourceView setMarkers:[_manager breakpointsForFile:_selection.file]];
}
@end
*/
- (void)addBreakpoint:(Breakpoint*)bp;
{
- if (![_breakpoints containsObject:bp])
- {
- [self willChangeValueForKey:@"breakpoints"];
- [_breakpoints addObject:bp];
- [self didChangeValueForKey:@"breakpoints"];
+ if ([_breakpoints containsObject:bp])
+ return;
+
+ if (bp.type == kBreakpointTypeFile && !bp.secureBookmark) {
+ // There is no secure bookmark for this file, so first see if any other
+ // bookmarks exist for the same file. If not, try and create a bookmark.
+ if (!bp.secureBookmark) {
+ [bp createSecureBookmark];
+ /*
+ for (Breakpoint* other in _breakpoints) {
+ if ([other.file isEqualToString:bp.file] && other.secureBookmark) {
+ bp.secureBookmark = other.secureBookmark;
+ break;
+ }
+ }
- [_connection addBreakpoint:bp];
+ if (!bp.secureBookmark) {
+ [bp createSecureBookmark];
+ }
+ */
+ }
+ }
- [_savedBreakpoints addObject:[bp dictionary]];
- [[NSUserDefaults standardUserDefaults] setObject:_savedBreakpoints forKey:kPrefBreakpoints];
+ [self willChangeValueForKey:@"breakpoints"];
+ [_breakpoints addObject:bp];
+ [self didChangeValueForKey:@"breakpoints"];
- [self updateDisplaysForFile:[bp file]];
- }
+ [_connection addBreakpoint:bp];
+
+ [_savedBreakpoints addObject:[bp dictionary]];
+ [[NSUserDefaults standardUserDefaults] setObject:_savedBreakpoints forKey:kPrefBreakpoints];
+ [self updateDisplaysForFile:[bp file]];
}
- (Breakpoint*)removeBreakpoint:(Breakpoint*)bp
{
- if ([_breakpoints containsObject:bp]) {
- [self willChangeValueForKey:@"breakpoints"];
- [_breakpoints removeObject:bp];
- [self didChangeValueForKey:@"breakpoints"];
+ if (![_breakpoints containsObject:bp])
+ return nil;
- [_connection removeBreakpoint:bp];
+ [self willChangeValueForKey:@"breakpoints"];
+ [_breakpoints removeObject:bp];
+ [self didChangeValueForKey:@"breakpoints"];
- [_savedBreakpoints removeObject:[bp dictionary]];
- [[NSUserDefaults standardUserDefaults] setObject:_savedBreakpoints forKey:kPrefBreakpoints];
+ [_connection removeBreakpoint:bp];
- if (bp.file)
- [self updateDisplaysForFile:bp.file];
+ [_savedBreakpoints removeObject:[bp dictionary]];
+ [[NSUserDefaults standardUserDefaults] setObject:_savedBreakpoints forKey:kPrefBreakpoints];
- return bp;
- }
- return nil;
+ if (bp.file)
+ [self updateDisplaysForFile:bp.file];
+
+ return bp;
}
/**