RESOLVED INVALID 16440
File replacement support
https://bugs.webkit.org/show_bug.cgi?id=16440
Summary File replacement support
Vicki Murley
Reported 2007-12-14 14:33:30 PST
It would be great if there were a way to save files locally from within Drosera, and a way to tell Drosera to use those locally saved files next time the page is debugged. The ability to load locally modified subresources would make it a lot easier to isolate problems in web pages, and come up with reductions and test cases.
Attachments
with this changes .html,.js and .css files can be saved locally (5.52 KB, patch)
2008-02-01 15:08 PST, Sajesh
no flags
new patch after removing the platform specific classes (36.46 KB, patch)
2008-02-13 15:17 PST, Sajesh
darin: review-
implemented review comments (32.96 KB, patch)
2008-03-18 15:59 PDT, Sajesh
timothy: review-
Vicki Murley
Comment 1 2007-12-14 14:35:01 PST
<rdar://problem/5645671> ER: file replacement support
Vicki Murley
Comment 2 2007-12-14 17:20:27 PST
Or from within the Web Inspector.
David Kilzer (:ddkilzer)
Comment 3 2007-12-15 07:35:18 PST
(In reply to comment #0) > The ability to load locally modified subresources would make it a lot easier to > isolate problems in web pages, and come up with reductions and test cases. This enhancement request raises some really interesting points. I think what you're asking for (please correct me if I'm wrong) is to be able to save the state (DOM) of the current HTML page (as opposed to the original HTML loaded) with local copies of external resources (such as JavaScript source and stylesheets, in addition to images). New "Save As..." Formats It would be nice to support Firefox's "Web page, complete" format (Bug 7211), with an option to save the original HTML or the current DOM. Supporting the reading (Bug 7168) and writing (Bug 7169) of MSIE's MHTML format would work as well, although it's not in a form that's easily modifiable locally. Saving Original HTML The only current options for saving the original HTML (from Safari) are as Page Source and as a WebArchive file. Page Source does not include any external resources, and the WebArchive file has known bugs about saving external resources (Bug 11839, Bug 11850, Bug 12025) and is not an easy format to modify locally (Bug 7241 could provide a helper script). The WebArchive format isn't cross-platform, either. Alternative Approaches Another approach to saving external JavaScript or CSS stylesheets resources is to "flatten" (or "inline") them into the DOM, then save the current DOM to disk. (You could optionally turn images and other external resources into data: URLs, but that would clutter the HTML quite a bit.) JavaScript and CSS stylesheet flattening could be accomplished with JavaScript bookmarklets. (I have written JavaScript code to flatten CSS stylesheets into the DOM as part of a test case reduction tool, although it's not in the form of a bookmarklet.) Once the resources are flattened, you still need to save the current DOM to disk. There is no nice way to do this today, although you can accomplish that task using a JavaScript alert() dialog with Safari because it lets you copy the contents of the alert sheet using Cmd-A and Cmd-C, even if the content is too big to fit on the sheet: javascript:alert(document.documentElemenet.outerHTML)
Sajesh
Comment 4 2008-02-01 15:08:39 PST
Created attachment 18856 [details] with this changes .html,.js and .css files can be saved locally changes are done in 1. inspector.html/inspector.js - to add the check box to toggle the functionlity 2. InspectorController.cpp/InspectorController.h - to save the log
Sajesh
Comment 5 2008-02-01 15:11:30 PST
Comment on attachment 18856 [details] with this changes .html,.js and .css files can be saved locally Index: ChangeLog =================================================================== --- ChangeLog (revision 29927) +++ ChangeLog (working copy) @@ -1,3 +1,44 @@ +2008-02-01 apple <set EMAIL_ADDRESS environment variable> + + Reviewed by NOBODY (OOPS!). + + WARNING: NO TEST CASES ADDED OR CHANGED + + * html/HTMLLinkElement.cpp: + (WebCore::HTMLLinkElement::process): + * html/HTMLTokenizer.cpp: + (WebCore::HTMLTokenizer::scriptHandler): + * loader/FrameLoader.cpp: + (WebCore::FrameLoader::load): + * page/Frame.cpp: + (WebCore::Frame::Frame): + (WebCore::Frame::storeReplaceURLs): + * page/Frame.h: + * page/InspectorController.cpp: + (WebCore::log): + (WebCore::replace): + (WebCore::deleteAKey): + (WebCore::checkForAKey): + (WebCore::toggleJStrace): + (WebCore::InspectorController::windowScriptObjectAvailable): + (WebCore::InspectorController::updateStatusBar): + (WebCore::InspectorController::doAllStuffforReplace): + (WebCore::InspectorController::isFileExists): + (WebCore::InspectorController::isKeyPresent): + (WebCore::InspectorController::deleteKey): + (WebCore::InspectorController::getValueForKey): + (WebCore::InspectorController::getPlistURL): + (WebCore::InspectorController::readExistingPlist): + (WebCore::InspectorController::saveToPlist): + (WebCore::InspectorController::saveScriptsLocally): + (WebCore::InspectorController::getCurrentUserHomeDir): + (WebCore::InspectorController::toggleJSTrace): + * page/InspectorController.h: + * page/inspector/NetworkPanel.js: + * page/inspector/inspector.html: + * page/inspector/inspector.js: + 2008-02-01 Darin Adler <darin@apple.com> Reviewed by Tim. Index: html/HTMLLinkElement.cpp =================================================================== --- html/HTMLLinkElement.cpp (revision 29878) +++ html/HTMLLinkElement.cpp (working copy) @@ -190,6 +190,17 @@ void HTMLLinkElement::process() m_cachedSheet->deref(this); } m_loading = true; + + //Bug 16440: ER: file replacement support - for .css files + int index = m_url.find('?', 0); + String str = m_url.left(index); + CFStringRef keyVal = str.createCFString(); + CFStringRef newURL; + if( !document()->frame()->replaceCFPropListRef && CFDictionaryGetValueIfPresent((CFDictionaryRef)document()->frame()->replaceCFPropListRef, (void*)keyVal, (const void**)&newURL)) { + String newstr(newURL); + m_url = newstr; + } + m_cachedSheet = document()->docLoader()->requestCSSStyleSheet(m_url, chset); if (m_cachedSheet) m_cachedSheet->ref(this); Index: html/HTMLTokenizer.cpp =================================================================== --- html/HTMLTokenizer.cpp (revision 29878) +++ html/HTMLTokenizer.cpp (working copy) @@ -394,6 +394,15 @@ HTMLTokenizer::State HTMLTokenizer::scri if (!m_doc->ownerElement()) printf("Requesting script at time %d\n", m_doc->elapsedTime()); #endif + //Bug 16440: ER: file replacement support - for .js files + int index = scriptSrc.find('?', 0); + String str = scriptSrc.left(index); + CFStringRef keyVal = str.createCFString(); + CFStringRef newURL; + if( !m_doc->frame()->replaceCFPropListRef && CFDictionaryGetValueIfPresent((CFDictionaryRef)m_doc->frame()->replaceCFPropListRef, (void*)keyVal, (const void**)&newURL)) { + String newstr(newURL); + scriptSrc = newstr; + } // The parser might have been stopped by for example a window.close call in an earlier script. // If so, we don't want to load scripts. if (!m_parserStopped && (cs = m_doc->docLoader()->requestScript(scriptSrc, scriptSrcCharset))) Index: loader/FrameLoader.cpp =================================================================== --- loader/FrameLoader.cpp (revision 29878) +++ loader/FrameLoader.cpp (working copy) @@ -2154,6 +2154,17 @@ void FrameLoader::load(DocumentLoader* l ASSERT(m_client->hasFrameView()); m_policyLoadType = type; + + //Bug 16440: ER: file replacement support - for .html files + int index = loader->request().url().string().find('?', 0); + String str = loader->request().url().string().left(index); + CFStringRef keyVal = str.createCFString(); + CFStringRef newURL; + if ( !m_frame->replaceCFPropListRef && CFDictionaryGetValueIfPresent((CFDictionaryRef)m_frame->replaceCFPropListRef, (void*)keyVal, (const void**)&newURL)) { + String newstr(newURL); + KURL url(newstr.utf8().data()); + loader->request().setURL(url); + } if (Frame* parent = m_frame->tree()->parent()) loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding()); Index: page/Frame.cpp =================================================================== --- page/Frame.cpp (revision 29878) +++ page/Frame.cpp (working copy) @@ -173,6 +173,11 @@ Frame::Frame(Page* page, HTMLFrameOwnerE #ifndef NDEBUG ++FrameCounter::count; #endif + + //Hack to replace urls -start + replaceCFPropListRef=NULL; + storeReplaceURLs(); + //Hack to replace urls -end } Frame::~Frame() @@ -1885,6 +1890,30 @@ Document* Frame::documentAtPoint(const I result = eventHandler()->hitTestResultAtPoint(pt, false); return result.innerNode() ? result.innerNode()->document() : 0; } + +//Bug 16440: ER: file replacement support +void Frame::storeReplaceURLs() +{ + CFDataRef xmlCFDataRef; + Boolean status; + CFURLRef fileURL; + OSErr err; + FSRef prefFolderRef; + CFURLRef prefURL = NULL; + err = FSFindFolder(kUserDomain, kCurrentUserFolderType, kDontCreateFolder, &prefFolderRef); + if (err == noErr) + prefURL = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &prefFolderRef); + CFStringRef fsPath = CFURLCopyFileSystemPath(prefURL, kCFURLPOSIXPathStyle); + CFMutableStringRef filePath = CFStringCreateMutable(kCFAllocatorDefault, 0); + CFStringAppend(filePath, fsPath); + CFStringAppend(filePath, CFSTR("/Library/Preferences/filestoreplace.plist")); + fileURL = CFURLCreateWithFileSystemPath(NULL, filePath, kCFURLPOSIXPathStyle, false); + status = CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &xmlCFDataRef, NULL, NULL, NULL); + if (status) { + replaceCFPropListRef = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, xmlCFDataRef, kCFPropertyListImmutable, NULL); + CFRelease(xmlCFDataRef); + } + } FramePrivate::FramePrivate(Page* page, Frame* parent, Frame* thisFrame, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* frameLoaderClient) Index: page/Frame.h =================================================================== --- page/Frame.h (revision 29878) +++ page/Frame.h (working copy) @@ -378,6 +378,9 @@ public: NSDictionary* fontAttributesForSelectionStart() const; NSWritingDirection baseWritingDirectionForSelectionStart() const; void issuePasteCommand(); + //Bug 16440: ER: file replacement support + CFPropertyListRef replaceCFPropListRef; + void storeReplaceURLs(); #endif Index: page/InspectorController.cpp =================================================================== --- page/InspectorController.cpp (revision 29878) +++ page/InspectorController.cpp (working copy) @@ -60,6 +60,8 @@ #include <JavaScriptCore/JSRetainPtr.h> #include <JavaScriptCore/JSStringRef.h> #include <wtf/RefCounted.h> +#include <JavaScriptCore/JSStringRefCF.h> +#import <ApplicationServices/ApplicationServices.h> #if ENABLE(DATABASE) #include "Database.h" @@ -568,6 +570,136 @@ static JSValueRef moveByUnrestricted(JSC return JSValueMakeUndefined(ctx); } +static JSValueRef log(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef /*thisObject*/, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/) +{ + if (argumentCount < 1 || !JSValueIsString(ctx, arguments[0])) + return JSValueMakeUndefined(ctx); + +#ifndef NDEBUG + JSStringRef string = JSValueToStringCopy(ctx, arguments[0], 0); + String message(JSStringGetCharactersPtr(string), JSStringGetLength(string)); + JSStringRelease(string); + + fprintf(stderr, "%s\n", message.latin1().data()); +#endif + + return JSValueMakeUndefined(ctx); +} + +static JSValueRef replace(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/) +{ + InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); + if (!controller) + return JSValueMakeUndefined(ctx); + + if (argumentCount < 2 || !JSValueIsString(ctx, arguments[0]) ) + return JSValueMakeUndefined(ctx); + + JSStringRef replaceURL = JSValueToStringCopy(ctx, arguments[0], 0); + + JSValueRef identifierValue = arguments[1]; + if (!JSValueIsNumber(ctx, identifierValue)) + return JSValueMakeUndefined(ctx); + + controller->doAllStuffforReplace( replaceURL ); + + CFStringRef orgURL = JSStringCopyCFString(kCFAllocatorDefault, replaceURL ); + + CFURLRef urlForExtn = CFURLCreateWithString( kCFAllocatorDefault, orgURL, NULL); + CFStringRef urlExtn = CFURLCopyPathExtension(urlForExtn); + + if ( CFStringCompare(urlExtn,CFSTR("css"),1) == kCFCompareEqualTo || CFStringCompare(urlExtn,CFSTR("htm"),1) == kCFCompareEqualTo || CFStringCompare(urlExtn,CFSTR("js"),1) == kCFCompareEqualTo || CFStringCompare(urlExtn,CFSTR("html"),1) == kCFCompareEqualTo) + { + + unsigned long identifier = static_cast<unsigned long>(JSValueToNumber(ctx, identifierValue, 0)); + RefPtr<InspectorResource> resource = controller->resources().get(identifier); + ASSERT(resource); + if (!resource) + return JSValueMakeUndefined(ctx); + + RefPtr<SharedBuffer> buffer; + String textEncodingName; + if (resource->requestURL == resource->loader->requestURL()) { + buffer = resource->loader->mainResourceData(); + textEncodingName = resource->loader->frame()->document()->inputEncoding(); + } else { + FrameLoader* frameLoader = resource->loader->frameLoader(); + if (!frameLoader) + return JSValueMakeUndefined(ctx); + + Document* doc = frameLoader->frame()->document(); + if (!doc) + return JSValueMakeUndefined(ctx);; + + CachedResource* cachedResource = doc->docLoader()->cachedResource(resource->requestURL.string()); + if (!cachedResource) + return JSValueMakeUndefined(ctx); + + buffer = cachedResource->data(); + textEncodingName = cachedResource->encoding(); + } + + if (!buffer) + return JSValueMakeUndefined(ctx);; + + TextEncoding encoding(textEncodingName); + if (!encoding.isValid()) + encoding = WindowsLatin1Encoding(); + + String sourceString = encoding.decode(buffer->data(), buffer->size()); + JSStringRef urlSource = JSStringCreateWithCharacters(sourceString.characters(), sourceString.length()); + controller->saveScriptsLocally( replaceURL, urlSource ); + JSStringRelease(urlSource); + } + return JSValueMakeUndefined(ctx); +} + +static JSValueRef deleteAKey(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/) +{ + InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); + if (!controller) + return JSValueMakeUndefined(ctx); + + if (argumentCount < 1 || !JSValueIsString(ctx, arguments[0])) + return JSValueMakeUndefined(ctx); + + JSStringRef keyToDelete = JSValueToStringCopy(ctx, arguments[0], 0); + if (controller->isKeyPresent(keyToDelete)) + controller->deleteKey(keyToDelete); + + return JSValueMakeUndefined(ctx); +} + +static JSValueRef checkForAKey(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/) +{ + InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); + if (!controller) + return JSValueMakeUndefined(ctx); + + if (argumentCount < 1 || !JSValueIsString(ctx, arguments[0])) + return JSValueMakeUndefined(ctx); + + JSStringRef keyToCheck = JSValueToStringCopy(ctx, arguments[0], 0); + JSValueRef ret = JSValueMakeBoolean(ctx, controller->isKeyPresent( keyToCheck )); + + return ret; +} + +static JSValueRef toggleJStrace(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/) +{ + InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); + if (!controller) + return JSValueMakeUndefined(ctx); + + if (argumentCount < 1 || !JSValueIsBoolean(ctx, arguments[0])) + return JSValueMakeUndefined(ctx); + + bool toggle = JSValueToBoolean( ctx, arguments[0] ); + controller->toggleJSTrace(toggle); + + return JSValueMakeUndefined(ctx); +} + #pragma mark - #pragma mark InspectorController Class @@ -755,6 +887,7 @@ void InspectorController::windowScriptOb { "windowUnloading", unloading, kJSPropertyAttributeNone }, { "attach", attach, kJSPropertyAttributeNone }, { "detach", detach, kJSPropertyAttributeNone }, + { "log", log, kJSPropertyAttributeNone }, { "search", search, kJSPropertyAttributeNone }, #if ENABLE(DATABASE) { "databaseTableNames", databaseTableNames, kJSPropertyAttributeNone }, @@ -763,6 +896,10 @@ void InspectorController::windowScriptOb { "localizedStringsURL", localizedStrings, kJSPropertyAttributeNone }, { "platform", platform, kJSPropertyAttributeNone }, { "moveByUnrestricted", moveByUnrestricted, kJSPropertyAttributeNone }, + { "replaceURL", replace, kJSPropertyAttributeNone }, + { "deleteKey", deleteAKey, kJSPropertyAttributeNone }, + { "checkKey", checkForAKey, kJSPropertyAttributeNone }, + { "toggleJStrace", toggleJStrace, kJSPropertyAttributeNone }, { 0, 0, 0 } }; @@ -1572,4 +1709,282 @@ void InspectorController::moveWindowBy(f m_page->chrome()->setWindowRect(frameRect); } + +CFStringRef InspectorController::updateStatusBar() +{ + int resSize = m_resources.size(); + char statusTxtBuffer[1024]; + CFStringRef statusTxt = NULL; + + if( resSize > 0 ) { + int count = 0; + for (int i=1; i<=resSize; i++) { + RefPtr<InspectorResource> resource = m_resources.get(i); + if (!resource) + return statusTxt; + + else { + String urlName = resource->requestURL.string(); + JSStringRef urlNameJSString = JSStringCreateWithCharacters(urlName.characters(), urlName.length()); + CFStringRef urlNameCFString = JSStringCopyCFString(kCFAllocatorDefault, urlNameJSString); + urlNameCFString = CFURLCreateStringByReplacingPercentEscapes (kCFAllocatorSystemDefault, urlNameCFString,CFSTR("")); + urlNameJSString = JSStringCreateWithCFString(urlNameCFString); + if (isKeyPresent(urlNameJSString)) + count++; + } + } + + sprintf(statusTxtBuffer, "%d files loaded locally", count); + statusTxt = CFStringCreateWithCString(kCFAllocatorSystemDefault, statusTxtBuffer, kCFStringEncodingUTF8); + } + + return statusTxt; +} + +void InspectorController::doAllStuffforReplace(JSStringRef url) +{ + CFStringRef orgURL = JSStringCopyCFString(kCFAllocatorDefault, url); + CFMutableDictionaryRef aDict; + + CFURLRef fileURL = getPlistURL(); + + if (isFileExists(fileURL)) { + aDict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, readExistingPlist()); + if ( !CFDictionaryContainsKey(aDict, (const void *)orgURL) && !CFDictionaryContainsValue(aDict, (const void *)orgURL)) { + const void *newKey = (const void *)orgURL; + const void *newValue = (const void *)getValueForKey(orgURL); + CFDictionaryAddValue(aDict, newKey, newValue); + saveToPlist(aDict); + } + }else + { + aDict= CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + const void *newKey = (const void *)orgURL; + const void *newValue = (const void *)getValueForKey(orgURL); + CFDictionaryAddValue(aDict, newKey, newValue); + saveToPlist(aDict); + } + + CFRelease(aDict); +} + +bool InspectorController::isFileExists(CFURLRef fileURL) +{ + CFDataRef xmlCFDataRef; + Boolean status = FALSE; + SInt32 errorCode = -10; + + status = CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL,&xmlCFDataRef, NULL, NULL, &errorCode); + + if ( errorCode < 0 ) + return false; + else + return true; +} + +bool InspectorController::isKeyPresent(JSStringRef key) +{ + CFStringRef keyToCheck = JSStringCopyCFString(kCFAllocatorDefault, key); + CFURLRef fileURL = getPlistURL(); + + if (isFileExists(fileURL)) { + CFDictionaryRef aDict; + aDict = readExistingPlist(); + + if (CFDictionaryContainsKey(aDict, (const void *)keyToCheck) || CFDictionaryContainsValue(aDict, (const void *)keyToCheck)) + return true; + else + return false; + CFRelease(aDict); + }else + return false; +} + +void InspectorController::deleteKey(JSStringRef key) +{ + CFStringRef keyToDelete = JSStringCopyCFString(kCFAllocatorDefault, key); + CFURLRef fileURL = getPlistURL(); + + if (isFileExists(fileURL)) { + CFMutableDictionaryRef aDict; + aDict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, readExistingPlist()); + const void *delKey = (const void *)keyToDelete; + + if(CFDictionaryContainsKey(aDict, delKey)) { + CFDictionaryRemoveValue(aDict, delKey); + saveToPlist(aDict); + }else + + if ( CFDictionaryContainsValue(aDict, delKey)) { + int size; + CFStringRef toDelKey = NULL; + size = CFDictionaryGetCount(aDict); + + if (size > 0) { + CFStringRef outKeys[size]; + CFStringRef outValues[size]; + CFDictionaryGetKeysAndValues(aDict, (const void**)&outKeys, (const void**)&outValues); + for ( int i=0; i<size; i++) { + if (CFStringCompare(outValues[i],keyToDelete,1) == kCFCompareEqualTo) + toDelKey = outKeys[i]; + } + CFDictionaryRemoveValue(aDict, toDelKey); + saveToPlist(aDict); + } + } + + CFRelease(aDict); + } +} + +CFStringRef InspectorController::getValueForKey( CFStringRef url ) +{ + CFStringRef sperator = CFSTR("/"); + CFArrayRef array = CFStringCreateArrayBySeparatingStrings(NULL, url, sperator); + CFStringRef fileName = (CFStringRef)CFArrayGetValueAtIndex(array, CFArrayGetCount(array) - 1); + CFStringRef currentUserName = CSCopyUserName(FALSE); + CFMutableStringRef filePath = CFStringCreateMutable (kCFAllocatorDefault, 0); + + CFStringAppend(filePath, CFSTR("http://localhost/~")); + CFStringAppend(filePath, currentUserName); + CFStringAppend(filePath, CFSTR("/filestoreplace/")); + CFStringAppend(filePath, fileName); + + return filePath; +} + +CFURLRef InspectorController::getPlistURL() +{ + CFURLRef fileURL; + CFMutableStringRef filePath = CFStringCreateMutable (kCFAllocatorDefault, 0); + CFStringRef homeDir = getCurrentUserHomeDir(); + + CFStringAppend(filePath, homeDir); + CFStringAppend(filePath, CFSTR("/Library/Preferences/filestoreplace.plist")); + + fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePath, kCFURLPOSIXPathStyle, false); + + return fileURL; +} + +CFDictionaryRef InspectorController::readExistingPlist() +{ + CFDataRef xmlCFDataRef; + CFPropertyListRef myCFPropertyListRef = NULL; + Boolean status = FALSE; + CFURLRef fileURL = getPlistURL(); + SInt32 errorCode = -10; + + status = CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL,&xmlCFDataRef, NULL, NULL, &errorCode); + + if (status) { + myCFPropertyListRef = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, xmlCFDataRef,kCFPropertyListImmutable, NULL); + CFRelease(xmlCFDataRef); + } + + return (CFDictionaryRef)myCFPropertyListRef; +} + +void InspectorController::saveToPlist(CFMutableDictionaryRef dict) +{ + CFDataRef xmlData; + Boolean status = FALSE; + SInt32 errorCode = -10; + CFURLRef fileURL = getPlistURL(); + + xmlData = CFPropertyListCreateXMLData(kCFAllocatorDefault, (CFDictionaryRef)dict); + + if (xmlData) { + status = CFURLWriteDataAndPropertiesToResource(fileURL, xmlData, NULL, &errorCode); + CFRelease(xmlData); + } +} + +void InspectorController::saveScriptsLocally( JSStringRef url, JSStringRef source ) +{ + SInt32 errorCode = -10; + CFStringRef cfURL = JSStringCopyCFString(kCFAllocatorDefault, url); + CFStringRef sperator = CFSTR("/"); + CFArrayRef array = CFStringCreateArrayBySeparatingStrings(NULL, cfURL, sperator); + CFStringRef fileName = (CFStringRef)CFArrayGetValueAtIndex(array, CFArrayGetCount(array)-1); + CFStringRef homeDir = getCurrentUserHomeDir(); + CFMutableStringRef dirToSave = CFStringCreateMutable(kCFAllocatorDefault, 0); + CFMutableStringRef filePath = CFStringCreateMutable(kCFAllocatorDefault, 0); + + CFStringAppend(dirToSave, homeDir); + CFStringAppend(dirToSave, CFSTR("/Sites/filestoreplace/")); + CFStringAppend(filePath, dirToSave); + CFStringAppend(filePath, CFSTR("/")); + CFStringAppend(filePath, fileName); + + CFURLRef fileToSave = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePath ,kCFURLPOSIXPathStyle, false);//for file + CFURLRef dir = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, dirToSave ,kCFURLPOSIXPathStyle, true );//for creating dir if it not exist + + if (!isFileExists(fileToSave)) { + CFStringRef cfURLSource = JSStringCopyCFString(kCFAllocatorDefault, source); + CFDataRef dataToWrite = NULL; + + if (cfURLSource) { + dataToWrite = CFStringCreateExternalRepresentation(kCFAllocatorDefault, cfURLSource, kCFStringEncodingWindowsLatin1, 0); + CFURLWriteDataAndPropertiesToResource(dir, dataToWrite, NULL, &errorCode ); + CFURLWriteDataAndPropertiesToResource (fileToSave, dataToWrite, NULL, &errorCode); + } + CFRelease(dataToWrite); + } +} + +CFStringRef InspectorController::getCurrentUserHomeDir() +{ + OSErr err; + FSRef prefFolderRef; + CFURLRef prefURL = NULL; + + err = FSFindFolder(kUserDomain, kCurrentUserFolderType, kDontCreateFolder, &prefFolderRef); + + if (err == noErr) + prefURL = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &prefFolderRef); + + CFStringRef fsPath = CFURLCopyFileSystemPath (prefURL, kCFURLPOSIXPathStyle); + + return fsPath; +} + +void InspectorController::toggleJSTrace(bool toggle) +{ + //to enable and disable trace + CFStringRef homeDir = getCurrentUserHomeDir(); + CFMutableStringRef logFile = CFStringCreateMutable (kCFAllocatorDefault, 0); + + CFStringAppend(logFile, homeDir); + CFStringAppend(logFile, CFSTR("/Library/Logs/webkit-javascript-stack-trace.log")); + const char *logDir = CFStringGetCStringPtr(logFile, NULL); + setenv("JSHOME", logDir, 1); + + if ( toggle ) + setenv("JSTRACE", "YES", 1); + else + setenv("JSTRACE", "NO", 1); +} + + } // namespace WebCore Index: page/InspectorController.h =================================================================== --- page/InspectorController.h (revision 29878) +++ page/InspectorController.h (working copy) @@ -115,6 +115,21 @@ public: void didOpenDatabase(Database*, const String& domain, const String& name, const String& version); #endif + //Bug 16440: ER: file replacement support & Bug 16441: ER: support for saving js stack trace to file + bool isKeyPresent(JSStringRef key); + bool isFileExists(CFURLRef fileURL); + void doAllStuffforReplace(JSStringRef orgURL); + void saveToPlist(CFMutableDictionaryRef dict); + void deleteKey(JSStringRef key); + void saveScriptsLocally(JSStringRef url, JSStringRef source); + void toggleJSTrace(bool toggle); + CFDictionaryRef readExistingPlist(); + CFStringRef getValueForKey(CFStringRef url); + CFURLRef getPlistURL(); + CFStringRef getCurrentUserHomeDir(); + CFStringRef updateStatusBar( ); + const ResourcesMap& resources() const { return m_resources; } void moveWindowBy(float x, float y) const; Index: page/inspector/NetworkPanel.js =================================================================== --- page/inspector/NetworkPanel.js (revision 29878) +++ page/inspector/NetworkPanel.js (working copy) @@ -682,10 +682,29 @@ WebInspector.NetworkTimelineEntry = func this.titleElement = document.createElement("div"); this.titleElement.className = "network-title"; this.resourceElement.appendChild(this.titleElement); - + var checkBoxString = "<INPUT type='checkbox' id='replaceChkBox' title='Add/Remove from plist' "; + var strUrl=resource.url.toLowerCase(); + if (strUrl.indexOf(".css")>0 ||strUrl.indexOf(".js")>0 || strUrl.indexOf(".html")>0 || strUrl.indexOf(".htm")>0 ) + { + checkBoxString += "onclick='WebInspector.replaceFileLocally("+"\""+resource.url+"\","+resource.identifier+")'"; + if ( WebInspector.isKeyPresentInPlist(resource.url) ) + { + checkBoxString += "checked='checked'"; + } + else + { + checkBoxString += ""; + } + } + else + { + checkBoxString += "disabled='true'"; + } + + checkBoxString +="></INPUT>"; this.fileElement = document.createElement("div"); this.fileElement.className = "network-file"; - this.fileElement.innerHTML = WebInspector.linkifyURL(resource.url, resource.displayName); + this.fileElement.innerHTML = checkBoxString + WebInspector.linkifyURL(resource.url, resource.displayName); this.titleElement.appendChild(this.fileElement); this.tipButtonElement = document.createElement("button"); Index: page/inspector/inspector.html =================================================================== --- page/inspector/inspector.html (revision 29878) +++ page/inspector/inspector.html (working copy) @@ -54,6 +54,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE PO <body class="detached"> <div id="toolbar"> <button id="back" class="split-button first"><img></button><img class="split-button-divider"><button id="forward" class="split-button last"><img></button> + <input type='checkbox' value='Save JS' id='chkBox' title='Save JS trace' onclick="WebInspector.toggleJStrace()"></input> + <label for='chkBox'>Save JS trace</label> <span id="toolbarButtons"></span> <input id="search" type="search" autosave="inspectorSearch" results="20" incremental="incremental" onsearch="WebInspector.performSearch(this.value)"> </div> Index: page/inspector/inspector.js =================================================================== --- page/inspector/inspector.js (revision 29878) +++ page/inspector/inspector.js (working copy) @@ -1184,3 +1184,59 @@ WebInspector.MIMETypes = { "text/jscript": {4: true}, "text/livescript": {4: true}, } +WebInspector.replaceFileLocally = function(url,identifier) +{ + var str=url; + var indx = str.indexOf('?'); + if(indx>0) + { + str=str.substring(0,indx); + } + + + if ( !event.target.checked ) + { + InspectorController.deleteKey(str); + event.stopPropagation(); + } + else + { + InspectorController.replaceURL(str,identifier); + //InspectorController.log(str); + event.stopPropagation(); + } +} + +WebInspector.isKeyPresentInPlist = function(url) +{ + var str=url; + var resourceID = this.resourceForURL(url); + var indx = str.indexOf('?'); + if(indx>0) + { + str=str.substring(0,indx); + } + + if ( InspectorController.checkKey( str ) ) + return true; + else + return false; +} + +WebInspector.log = function(str) +{ + InspectorController.log( str ) ; +} + +WebInspector.toggleJStrace = function() +{ + if ( event.target.checked ) + { + InspectorController.toggleJStrace( true ) ; + } + else + { + InspectorController.toggleJStrace( false ) ; + } +} +
Dave Hyatt
Comment 6 2008-02-01 15:48:22 PST
You can't use CFString inside cross-platform WebCore code (like the HTMLTokenizer).
Dave Hyatt
Comment 7 2008-02-01 15:49:52 PST
In general this patch is dropping a bunch of platform-specific code all over the place where it should not be. You will need to rewrite the patch to use WebCore's cross-platform classes.
Sajesh
Comment 8 2008-02-13 15:17:40 PST
Created attachment 19116 [details] new patch after removing the platform specific classes The platform specific class like CFString, CFURLRef, etc are now moved to FrameMac.mm class. the platform independent classes of WebCore is independent of CF classes
Mark Rowe (bdash)
Comment 9 2008-03-12 17:26:26 PDT
Please see <http://webkit.org/coding/contributing.html> for information about the patch contribution process. In particular, see the coding style guidelines at <http://webkit.org/coding/coding-style.html>. They cover naming and formatting of code. You should also add a ChangeLog entry with information about your change, with your email address in the header rather than the comment about setting your email address. When attaching a patch you should also set the review flag to "?" to indicate that it is ready to be reviewed.
Darin Adler
Comment 10 2008-03-13 10:07:01 PDT
Comment on attachment 19116 [details] new patch after removing the platform specific classes Thanks for contributing to WebKit! This patch has a lot of problems. + //Bug 16440: ER: file replacement support - for .css files We put spaces after "//" and we don't tag code with the bug number of the bug used to add the code. + if( document()->frame()->isReplaceCFPropListReady && document()->frame()->isURLPresentinDic(keyURL)) { No space after the parenthesis. + if( m_doc->frame()->isReplaceCFPropListReady && m_doc->frame()->isURLPresentinDic(keyURL)) { Formatting done here with tags. + String newstr = m_frame->getValueURLfromDic(keyURL); + KURL url(newstr.utf8().data()); This is the wrong way to convert a string to a KURL. There's no reason to convert the string to UTF-8 just to construct the URL. + m_frame->setJSStatusBarText(page->inspectorController()->setJSStatusBarTextForLoalload()); What's a "loalload"? It's not appropriate to set status bar text directly. Putting status bar text in at this time would be a policy decision for the web browser, not for WebKit itself. The setJSStatusBarText function is specifically to pass status bar text from JavaScript to the browser, and must not be used to add other status messages. + + //Hack to replace urls -start + isReplaceCFPropListReady=FALSE; + storeReplaceURL(); Unclear comment. + bool isURLPresentinDic(String keyURL); + String getValueURLfromDic( String keyURL); + void storeReplaceURL(); + bool isReplaceCFPropListReady; + String getPlistURL(); + String getCurrentUserHomeDir(); + bool isFileTobeReplaced( JSStringRef replaceURL ); + bool isFileExists(JSStringRef file); + + void addToPlist(JSStringRef urlToAdd); + String getValueForKey( String url ); + bool isKeyPresent( JSStringRef key); + void deleteAKeyFromPlist(JSStringRef key); + void saveScriptsLocally( JSStringRef url, JSStringRef source ); Lots wrong here: 1) We are not adding new functions and data to Frame at this time. Functions need to go elsewhere, on one of the appropriate Frame sub-objects. I don't understand this feature you're adding, so it's hard to know where it would go, but if it's about loading it probably belongs on FrameLoader. 2) These function names have abbreviations and style inconsistent with the rest of WebKit. For example, we don't use the word "Dic" and I have no idea what that refers to. 3) Data members should be private, not public. 4) JavaScript types like JSStringRef should not be used in general loader code. 5) We don't use "get" in the titles of functions that return values. 6) String parameters should be "const String&" rather than just string. 7) getValueForKey, for example, is way too generic a title for a function on Frame. What kind of value? What kind of key? A Frame is not a dictionary. 8) The references to "plist" are inappropriate for cross-platform code. That's a Macintosh-specific concept. All this new code for the development tools probably doesn't belong in the core WebKit, but in some other class on the side. You should talk with some of the folks working on the inspector and get their suggestions about where to put this code. My first thought is that editing of a local copy of a file should probably be lower level down at the loader/cache (classes like Loader and CachedResource) rather up in the frame and frame loader.
Sajesh
Comment 11 2008-03-18 15:57:30 PDT
(In reply to comment #10) > (From update of attachment 19116 [details] [edit]) > Thanks for contributing to WebKit! > > This patch has a lot of problems. > > + //Bug 16440: ER: file replacement support - for .css files > > We put spaces after "//" and we don't tag code with the bug number of the bug > used to add the code. This is implemented > > + if( document()->frame()->isReplaceCFPropListReady && > document()->frame()->isURLPresentinDic(keyURL)) { > > No space after the parenthesis. > This is implemented > + if( m_doc->frame()->isReplaceCFPropListReady && > m_doc->frame()->isURLPresentinDic(keyURL)) { > > Formatting done here with tags. > This is implemented > + String newstr = m_frame->getValueURLfromDic(keyURL); > + KURL url(newstr.utf8().data()); > > This is the wrong way to convert a string to a KURL. There's no reason to > convert the string to UTF-8 just to construct the URL. > This is implemented > + > m_frame->setJSStatusBarText(page->inspectorController()->setJSStatusBarTextForLoalload()); > > What's a "loalload"? It's not appropriate to set status bar text directly. > Putting status bar text in at this time would be a policy decision for the web > browser, not for WebKit itself. The setJSStatusBarText function is specifically > to pass status bar text from JavaScript to the browser, and must not be used to > add other status messages. > Changing the status bar text is removed. > + > + //Hack to replace urls -start > + isReplaceCFPropListReady=FALSE; > + storeReplaceURL(); > > Unclear comment. This is taken care and comments are udpated > > + bool isURLPresentinDic(String keyURL); > + String getValueURLfromDic( String keyURL); > + void storeReplaceURL(); > + bool isReplaceCFPropListReady; > + String getPlistURL(); > + String getCurrentUserHomeDir(); > + bool isFileTobeReplaced( JSStringRef replaceURL ); > + bool isFileExists(JSStringRef file); > + > + void addToPlist(JSStringRef urlToAdd); > + String getValueForKey( String url ); > + bool isKeyPresent( JSStringRef key); > + void deleteAKeyFromPlist(JSStringRef key); > + void saveScriptsLocally( JSStringRef url, JSStringRef source ); > > Lots wrong here: > > 1) We are not adding new functions and data to Frame at this time. > Functions need to go elsewhere, on one of the appropriate Frame sub-objects. I > don't understand this feature you're adding, so it's hard to know where it > would go, but if it's about loading it probably belongs on FrameLoader. The functionality is to save script files from Inspector window to local file system and to relaod this local file while reloading the page. This is done for debugging script files locally. The loading part is done in the FrameLoader class . The saving of the files is done on FrameMac.mm as it requires access to the frame resource to get the source. > 2) These function names have abbreviations and style inconsistent with the > rest of WebKit. For example, we don't use the word "Dic" and I have no idea > what that refers to. Taken care of this, changed the names to suit the fucntionality > 3) Data members should be private, not public. This has been taken care. > 4) JavaScript types like JSStringRef should not be used in general loader > code. This has been taken care. > 5) We don't use "get" in the titles of functions that return values. This has been taken care. > 6) String parameters should be "const String&" rather than just string. This has been taken care > 7) getValueForKey, for example, is way too generic a title for a function > on Frame. What kind of value? What kind of key? A Frame is not a dictionary. This has been taken care > 8) The references to "plist" are inappropriate for cross-platform code. > That's a Macintosh-specific concept. This functionality is now supported in Mac OS only so its using plist and CFDictionry. This is used in Mac specific classes. > > All this new code for the development tools probably doesn't belong in the core > WebKit, but in some other class on the side. You should talk with some of the > folks working on the inspector and get their suggestions about where to put > this code. > Will discuss with Inspector windows team to get their suggestions and will incorporate that changes. > My first thought is that editing of a local copy of a file should probably be > lower level down at the loader/cache (classes like Loader and CachedResource) > rather up in the frame and frame loader. >
Sajesh
Comment 12 2008-03-18 15:59:29 PDT
Created attachment 19876 [details] implemented review comments
Timothy Hatcher
Comment 13 2008-03-24 19:58:12 PDT
Comment on attachment 19876 [details] implemented review comments This patch will break other platforms, since Frame::currentUserHomeDir() is only implemented on the Mac. I also don't think the current approach will be good enough to ship. Right now the URL is changed to a localhost URL instead of using the original, this will break some sites that require cross domain scripting. Also this feature would be better if it didn't touch the disk and require a local HTTP server running. This feature should only replace the data that is received from the load, in memory.
Timothy Hatcher
Comment 14 2008-05-17 09:42:42 PDT
This should be added to the Web Inspector now that Drosera has been replaced by its new debugger.
Brian Burg
Comment 15 2014-12-14 19:58:46 PST
Closing this bug as it is quite old and filed against the n-2 inspector UI. The design discussions don't really apply any more. Live editing, or something like it, is probably the way to support the use case. Separate bugs exist for that.
Note You need to log in before you can comment on or make changes to this bug.